Spring Security之前后端分离(二)JWT令牌

Spring Security之前后端分离(二)JWT令牌

传送门:

  1. 有关于Spring Security 基础配置:https://blog.csdn.net/MostSnails/article/details/126312877
  2. 基础配置默认官方使用Session方式,分布式服务请求是无状态故Session有点满足要求。本期实践JWT以Token方式请求认证。
  3. 什么是JWT、采用哪些部分组成?这里就不多叙述,自行百度。

如何配置

  1. 配置Token拦截器,在UsernamePasswordAuthenticationFilter过滤器前先执行Token拦截器,UsernamePasswordAuthenticationFilter在基础配置中有讲过。

  2. JWT生成器、解析器、Login请求下发Token

  3. Security配置主要关闭Session、添加Token拦截器入配置

配置Token拦截器

  1. 需要实现OncePerRequestFilter接口,拦截所有请求,检测Header中Authorization属性、验证属性(过期、非法)、放行进入UsernamePasswordAuthenticationFilter

  2. 没有Authorization属性,直接放行,进入下一个过滤器属于Security,Security会去验证当前URL是否可匿名访问或需要权限访问。

/**
 * Token过滤器
 * desc:验证请求Token合法性
 */
@Component
public class TokenPerRequestFilter extends OncePerRequestFilter {

    @Autowired
    private UserService userService;



    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //  获取Authorization属性
        String token = request.getHeader("Authorization");
        if (StringUtils.isNotEmpty(token)) {
            //解析Token获取载荷
            try {
                Claims claims = JwtUtil.parseJWT(token);
                //获取用户信息 在创建Token Subject是传入json  这里可以获取你json值 Subject是可以任何,怎么set 就怎么get
                String userName = claims.getSubject();
                UserDetails userDetails = userService.loadUser(userName);
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                //放入上下文
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);

            } catch (ExpiredJwtException exception) {
                //Token过期
                System.out.println("Token过期");
                return;
            } catch (Exception e) {
                //非法Token
                e.printStackTrace();
                System.out.println("非法Token");
                return;
            }
        }
        //放行
        filterChain.doFilter(request, response);
    }
}

JWT工具类

  1. 主要提供Token令牌生成、解析令牌获取有效载荷,过期时间
public class JwtUtil {
    //私钥
    private static String SECRET_KEY = "NiJoSecret";
    //过期时间60S
    private static Long EXPIRATION_TIME = 60*1000L;
    //签名
    private static String ISS = "MostSnails";

    /**
     * 生成JWT带超时间
     *
     * @param subject   有效载荷 (主题-JSON格式字符串)
     * @param ttlMillis 有效时间 null 默认使用EXPIRATION_TIME变量值
     * @return String Token
     */
    public static String createJWT(String subject, Long ttlMillis) {
        //签发时间
        long issuedAt = System.currentTimeMillis();
        //过期时间 = 签发时间 + 定义过期时间
        long expirationTime = (Objects.isNull(ttlMillis) || ttlMillis == 0 ? EXPIRATION_TIME : ttlMillis) + issuedAt;
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        SecretKey key = generalKey();
        return Jwts.builder().setIssuer(ISS)//签名
                .setIssuedAt(new Date(issuedAt))//签发时间
                .signWith(signatureAlgorithm, key)//加密方式HS256和私钥加密后AES
                .setExpiration(new Date(expirationTime))//过期时间
                .setSubject(subject)
                .compact();
    }

    /**
     * 生成加密后的秘钥 secretKey
     *
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getDecoder().decode(SECRET_KEY);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

    /**
     * 解析Token获取有效载荷(即主题)
     *
     * @param token
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String token) throws Exception {
        SecretKey secretKey = generalKey();
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(token)
                .getBody();
    }
}

Security配置

  1. 关闭Session会话、添加Token过滤器
/**
 * Security 配置类
 * 1.加入编码解码器
 * 2.自定义登陆认证 authenticationManager.authenticate会进入它
 * 3.请求过滤和授权配置
 * 4.加入AuthenticationManager管理器
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private CustomerAuthenticationProvider customerAuthenticationProvider;

    private TokenPerRequestFilter tokenPerRequestFilter;
    /**
     * 加密器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 自定义登陆认证器
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(customerAuthenticationProvider);
        //自定义获取用户信息
        //auth.userDetailsService(null);
    }

    /**
     * 基于JWT配置
     * 关闭sesiso
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //所有请求验证接口并关闭CSRF、放行/login 其余全部请求拦截
        http.authorizeRequests().antMatchers("/login").anonymous()
                .anyRequest().authenticated()
                .and().logout().permitAll() //允许所有用户登出
                .and().httpBasic()
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)//关闭Session
                .and()
                .addFilterBefore(tokenPerRequestFilter, UsernamePasswordAuthenticationFilter.class)//在UsernamePasswordAuthenticationFilter之前验证
                .csrf().disable();
    }

    @Autowired
    public void setCustomerAuthenticationProvider(CustomerAuthenticationProvider customerAuthenticationProvider) {
        this.customerAuthenticationProvider = customerAuthenticationProvider;
    }

    @Autowired
    public void setTokenPerRequestFilter(TokenPerRequestFilter tokenPerRequestFilter){
        this.tokenPerRequestFilter=tokenPerRequestFilter;
    }

    /**
     * 注入AuthenticationManager管理器
     **/
    @Override
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

}

运行登陆获取Token

public UserInfo login(LoginUserDTO userDTO) {
        //使用security框架自带的验证token生成器  也可以自定义。
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userDTO.getUserName(), userDTO.getPassword());
        Authentication authenticate = authenticationManager.authenticate(token);
        //放入Security上下文
        SecurityContextHolder.getContext().setAuthentication(authenticate);
        //获取登陆信息
        UserInfo userInfo = (UserInfo) authenticate.getPrincipal();
        //生成Token
        String jwt = JwtUtil.createJWT(userInfo.getUsername(), null);
        userInfo.setToken(jwt);
        return userInfo;
    }

Spring Security之前后端分离(二)JWT令牌_第1张图片

带上Token访问认证Test接口

Spring Security之前后端分离(二)JWT令牌_第2张图片

Github:点我直达

你可能感兴趣的:(Security,spring,java,spring,boot)