Spring JWT Auth

1. Jason Web Token.

2. 基于 Session 认证的缺点

  • 服务器内存保存 session 信息,用户量大时,内存开销也大。

  • session 只保存在某服务器上,分布式应用受限制。

  • CSRF 攻击。(cookie 被拦截)

3. 基于 Token 的认证机制

  • 不需要在服务器端保留用户认证信息、会话信息

  • 工作原理:服务器收到用户的用户名、密码后,发送给用户一个 Token,以后每次请求在 Header 中包含此 Token 即可。

4. JWT 结构

Header(Base64).Payload(Base64).Signintra
  • Header

    • 加密算法,如: HMAC, SHA256, RSA

    • 类型,一般是 JWT

    • 例子:

        {
            "alg": "HS256",
            "typ": "JWT"
        }
      
  • Payload (可包含三种类型的声明 (Claims))

  • Signature

    • 加密的参数:Base64 编码的 Header, Base64 编码的 Payload, 密码,Header 中指定的算法。

    • 非对称加密。

      • 解密一个信息,需要同时使用公钥和私钥。

      • 服务器把公钥公开发布,私钥保存好。

      • 客户把信息使用公钥加密,除了拥有私钥的服务器,其他人理论上无法解密,包括客户自己。

      • 数学原理:

        • 大素数 p 和大素数 q,从它们的积 p * q 去分解因子 p 和 q,是一个公认的数学难题。
        • 公钥是 p * q 的积,私钥是 p 和 q。但实际算法情况要复杂的多。
  • 使用

      Header => 'Authorization': 'Bearer ' + token
    

5. Spring Web Security 使用 JWT 验证登陆

(1). 配置 Web Security 及 JPA 认证用户

  • 参考 WebSecurity 章节的笔记。

  • 需要将 Session 设置为 STATELESS 状态。

    @Override
    protected void configure(HttpSecurity http) throws Exception{
        http. 
            //...
    
            //设置 SESSION 状态为 STATELESS,关闭 SESSION 功能,用于 JWT 认证。
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
    
            //...
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

(2). 创建 JwtUtil,用于 Token 相关生成与解析

@Component
public class JwtUtil {
    @Value("${jwt.secret}")
    private String secret;

    public String generateToken(Claims claims){
        return Jwts.builder().setClaims(claims)
                            .setExpiration(new Date(System.currentTimeMillis() + 300000))
                            .signWith(SignatureAlgorithm.HS512, secret)
                            .compact();
    }

    public Claims parseToken(String token){
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

(3). 创建验证 JWT 的 Filter

@Component
public class JwtAuthTokenFilter extends OncePerRequestFilter {
    private static final Logger logger = LoggerFactory.getLogger(JwtAuthTokenFilter.class);
    private final String tokenHeader = "Authorization";
    private final String tokenHead = "Bearer";

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private AuthUserDetailsService authUserDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {
        String authHeader = request.getHeader(tokenHeader);
        try {
            if (authHeader != null && authHeader.startsWith(tokenHead)) {
                String token = authHeader.substring(tokenHead.length());
                Claims claims = jwtUtil.parseToken(token);

                String username = claims.get("username").toString();
                String email = claims.get("email").toString();

                logger.info("Doing auth check in filter for {}, {}", username, email);

                if (username!= null && SecurityContextHolder.getContext().getAuthentication() == null){
                    UserDetails userDetails = authUserDetailsService.loadUserByUsername(email);
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                            userDetails, null, userDetails.getAuthorities()
                    );

                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            } else {
                logger.info("Auth Header no exist. Do nothing.");
            }
        } catch (Exception e){
            e.printStackTrace();
            logger.warn(e.getMessage());
        }

        chain.doFilter(request, response);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

(4). 注册 JwtAuthTokenFilter

@Configuration
@EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    @Autowired
    private JwtAuthTokenFilter jwtAuthTokenFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception{
       http.addFilterBefore(jwtAuthTokenFilter, UsernamePasswordAuthenticationFilter.class);

       //...
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
最近更新: 10/9/2018, 10:42:31 PM