2025 记一次纯后端限制没登录无法访问其他接口:SpringBoot+Mybaitis-Flex + Spring Security + JJW + Redis + Cookie

1、pom依赖

       
        
            org.springframework.boot
            spring-boot-starter-data-redis

        
        
            org.springframework.session
            spring-session-data-redis

        
        
        
            org.springframework.boot
            spring-boot-starter-security
        
        
        
            io.jsonwebtoken
            jjwt-api
            0.11.5
        
        
            io.jsonwebtoken
            jjwt-impl
            0.11.5
            runtime
        
        
            io.jsonwebtoken
            jjwt-jackson
            0.11.5
            runtime
        

      
        
            com.feiniaojin
            graceful-response
            3.5.2-boot2
        

 2、配置文件

spring.application.name=test
server.port=8080

# DataSource Config
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
spring.main.allow-circular-references:true

# Redis config
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
spring.session.store-type=redis
server.servlet.session.timeout=1800

#graceful  GracefulResponseException 可选看你自己需不需要
graceful-response.rqesponse-class-full-name
graceful-response.response-style=1
graceful-response.default-success-msg=success
graceful-response.default-success-code=200
graceful-response.default-error-msg=error
graceful-response.default-error-code=201
graceful-response.origin-exception-using-detail-message=true
print-exception-in-global-advice:false


3、五个工具类

①JwtAuthenticationFilter


import com.mybatisflex.core.query.QueryWrapper;
import com.自己的实体类.entity.User;
import com.自己的服务类.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static com.自己的实体类2.entity.table.MyUserDef.USER;

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Autowired
    private JwtUtils jwtUtils;
    @Autowired
    private RedisService redisService;
    @Autowired
    private UserService userService; // 你的用户服务

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        String token = getJwtFromRequest(request);

        if (token != null && jwtUtils.validateToken(token)) {
            String username = jwtUtils.getUsernameFromToken(token);

           
                // 从数据库加载用户信息
                QueryWrapper queryWrapper = QueryWrapper.create()
                        .select(USER.ALL_COLUMNS)
                        .from(USER)
                        .where(USER.NAME.eq(username));
                User user = userService.getOne(queryWrapper);

                if (user != null) {
                    // 创建认证信息,注意第三个参数需要提供用户权限
                    List authorities = new ArrayList<>();
                    authorities.add(new SimpleGrantedAuthority("ADMIN")); // 添加默认角色

                    UsernamePasswordAuthenticationToken authentication =
                            new UsernamePasswordAuthenticationToken(
                                    user, null, authorities); // 这里传递权限列表
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
        
        }
        filterChain.doFilter(request, response);
    }

    private String getJwtFromRequest(HttpServletRequest request) {
        // 尝试从请求头获取
        String header = request.getHeader("Authorization");
        if (header != null && header.startsWith("Bearer ")) {
            return header.substring(7);
        }

        // 尝试从Cookie获取
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if ("Authorization".equals(cookie.getName())) {
                    return cookie.getValue();
                }
            }
        }

        return null;
    }
}

②JwtUtils 



import io.jsonwebtoken.*;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class JwtUtils {
    private static final String SECRET_KEY = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 建议从配置文件读取
    private static final long EXPIRATION_TIME = 86400000; // 24小时

    // 生成JWT
    public static String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }

    // 验证JWT
    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
            return true;
        } catch (MalformedJwtException | ExpiredJwtException |
                 UnsupportedJwtException | IllegalArgumentException e) {
            return false;
        }
    }

    // 从JWT中获取用户名
    public String getUsernameFromToken(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY)
                .parseClaimsJws(token).getBody().getSubject();
    }
}

 ③RedisService 



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class RedisService {
    private static final String TOKEN_PREFIX = "token:";
    private static final long EXPIRE_TIME = 86400; // 24小时
    @Autowired
    private StringRedisTemplate redisTemplate;

    // 存储Token到Redis
    public void saveToken(String username, String token) {
        redisTemplate.opsForValue().set(TOKEN_PREFIX + username, token, EXPIRE_TIME, TimeUnit.SECONDS);
    }

    // 从Redis获取Token
    public String getToken(String username) {
        return redisTemplate.opsForValue().get(TOKEN_PREFIX + username);
    }

    // 删除Token
    public void deleteToken(String username) {
        redisTemplate.delete(TOKEN_PREFIX + username);
    }

    // 验证Token
    public boolean validateToken(String username, String token) {
        String storedToken = getToken(username);
        return storedToken != null && storedToken.equals(token);
    }
}

④SecurityConfig 



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.Collections;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors().and() // 启用CORS
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/douyu_sanxia/login/toLogin").permitAll() // 允许登录接口匿名访问
                .anyRequest().authenticated() // 其他接口需要认证
                .and()
                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOriginPattern("*"); // 允许所有域名进行跨域调用
        config.addAllowedHeader("*"); // 允许任何请求头
        config.addAllowedMethod("*"); // 允许任何方法(POST、GET等)
        config.setAllowCredentials(true); // 允许携带凭证

        // 注意:当allowCredentials为true时,allowedOriginPatterns不能使用通配符*
        // 如果需要允许所有域名,建议改用以下方式:
        config.setAllowedOriginPatterns(Collections.singletonList("*"));
        config.setAllowedHeaders(Collections.singletonList("*"));
        config.setAllowedMethods(Collections.singletonList("*"));

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config); // 对所有接口都有效

        return new CorsFilter(source);
    }
}

⑤GlobalCorsConfig



import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 开启全局跨域配置文件
 */
@Configuration
public class GlobalCorsConfig implements WebMvcConfigurer {
    static final String ORIGINS[] = new String[]{"GET", "POST", "PUT", "DELETE", "OPTIONS"};

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 所有的当前站点的请求地址,都支持跨域访问
        registry.addMapping("/**")
                // 所有的外部域都可跨域访问 如果是localhost则很难配置,因为在跨域请求的时候,外部域的解析可能是localhost、127.0.0.1、主机名
                .allowedOriginPatterns("*")
                // 是否支持跨域预约凭证
                .allowCredentials(true)
                // 当前站点支持的跨域请求类型是什么
                .allowedMethods(ORIGINS)
                // 超时时长设置为1小时 时间单位是秒
                .maxAge(3600);
    }


}

4、接口逻辑【仅供参考】

GracefulResponseException可选可不选,看你需要



import com.feiniaojin.gracefulresponse.GracefulResponseException;
import com.mybatisflex.core.query.QueryWrapper;
import com.自己的实体类.entity.User;
import com.自己的实体VO类.entity.vo.LoginUserVo;
import com.自己的服务类.service.UserService;
import com.test.utils.jwt.JwtUtils;
import com.test.utils.jwt.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

import static com.自己的实体类2.entity.table.MyUserDef.USER;

/**
 * 登录信息
 *
 */
@RestController
@RequestMapping("/test/login")
public class LoginController {
    @Autowired
    private UserService userService;
    @Autowired
    private RedisService redisService;

    /**
     * 登录
     *
     * @param loginUserVo 登录的vo类
     * @return
     * @throws Exception
     */
    @PostMapping("/toLogin")
    public User toLogin(@RequestBody LoginUserVo loginUserVo, HttpServletResponse response) throws Exception {

        QueryWrapper queryWrapper = QueryWrapper.create()
                .select(USER.ALL_COLUMNS)
                .from(USER)
                .where(USER.NAME.eq(loginUserVo.getUsername()));
        User user = userService.getOne(queryWrapper);
        if (user == null) {
            throw new GracefulResponseException("用户不存在");
        }
        if (!user.getPwd().equals(loginUserVo.getPassword())) {
            throw new GracefulResponseException("密码错误");
        }
        // 生成JWT
        String token = JwtUtils.generateToken(user.getName());
        // 存入Redis
        redisService.saveToken(user.getName(), token); // 添加这一行

        // 创建Cookie存储Token
        Cookie tokenCookie = new Cookie("Authorization", token);
        tokenCookie.setHttpOnly(true); // 防止XSS攻击
        tokenCookie.setSecure(false);  // 开发环境使用HTTP时设置为false
        tokenCookie.setPath("/");      // 整个应用都可以访问
        tokenCookie.setMaxAge(30 * 60); // 设置有效期:30min * 60s = 1800s = 30分钟
        response.addCookie(tokenCookie);
        return user;
    }

}

你可能感兴趣的:(java工作中的难点,spring,boot,redis,java,SpringBoot,Mybaitis-Flex,Spring,Security,JJW)