本文共 5194 字,大约阅读时间需要 17 分钟。
传统的B/S应用开发采用Session进行状态管理,通过cookie和sessionid实现用户状态的存取。这种方式在非浏览器客户端和集群应用中存在诸多问题,如无法自动维护cookie,且session不可共享。
JWT(JSON Web Token)是一种用于传输用户身份信息的加密令牌,广泛应用于无状态的前后端分离应用开发。通过JWT,客户端无需存储任何状态信息,服务端仅需解签验证令牌即可获取用户信息。
JWT由三部分组成:Header(头)、Payload(有效载荷)和Signature(签名)。Header包含算法信息,Payload携带用户标识和其他自定义信息,Signature用于防止数据篡改。
JWT的安全性依赖于密钥的保密性。通过HTTPS传输和定期更换密钥,可以有效提升JWT的安全性。服务端需妥善保护密钥,防止泄露。
客户端通过用户名密码申请JWT,服务端验证后生成令牌并返回。Spring Security通过AuthenticationManager进行认证,生成JWT后返回给客户端。
客户端将JWT放在HTTP请求头中,服务端通过 JwtRequestFilter 拦截请求,解签验证令牌,结合UserDetailsService获取用户信息,完成接口权限校验。
开发JwtTokenUtil类,用于生成、解签、刷新JWT令牌。类似代码示例如下:
import io.jsonwebtoken.Claims;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import org.springframework.security.core.userdetails.UserDetails;import java.util.Map;import java.util.Date;@Data@Componentpublic class JwtTokenUtil { private String secret; private Long expiration; private String header; public String generateToken(UserDetails userDetails) { Map claims = new HashMap<>(); claims.put("sub", userDetails.getUsername()); claims.put("created", new Date()); return generateToken(claims); } public String getUsernameFromToken(String token) { Claims claims = getClaimsFromToken(token); return claims.getSubject(); } public Boolean isTokenExpired(String token) { Claims claims = getClaimsFromToken(token); return claims.getExpiration().before(new Date()); } public String refreshToken(String oldToken) { Claims claims = getClaimsFromToken(oldToken); claims.put("created", new Date()); return generateToken(claims); } public Boolean validateToken(String token, UserDetails userDetails) { String username = getUsernameFromToken(token); return username.equals(userDetails.getUsername()) && !isTokenExpired(token); } private String generateToken(Map claims) { Date expirationDate = new Date(System.currentTimeMillis() + expiration); return Jwts.builder() .setClaims(claims) .setExpirationDate(expirationDate) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } private Claims getClaimsFromToken(String token) { Claims claims = null; try { claims = Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); } catch (Exception e) { claims = null; } return claims; }} 定义JwtAuthController类,实现登录和令牌刷新接口。代码示例如下:
@RestControllerpublic class JwtAuthController { @Resource private JwtAuthService jwtAuthService; @PostMapping("/authentication") public AjaxResponse login(@RequestBody Map map) { String username = map.get("username"); String password = map.get("password"); if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { return AjaxResponse.error(new CustomException( CustomExceptionType.USER_INPUT_ERROR, "用户名密码不能为空")); } try { return AjaxResponse.success(jwtAuthService.login(username, password)); } catch (CustomException e) { return AjaxResponse.error(e); } } @PostMapping("/refreshtoken") public AjaxResponse refresh(@RequestHeader("${jwt.header}") String token) { return AjaxResponse.success(jwtAuthService.refreshToken(token)); }} 自定义JwtAuthenticationTokenFilter类,拦截请求解析JWT,校验用户权限,并通过Spring Security进行接口权限控制。代码示例如下:
@Componentpublic class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Resource private JwtTokenUtil jwtTokenUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String jwtToken = request.getHeader(jwtTokenUtil.getHeader()); if (jwtToken != null && !StringUtils.isEmpty(jwtToken)) { String username = jwtTokenUtil.getUsernameFromToken(jwtToken); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = jwtTokenUtil.myUserDetailsService.loadUserByUsername(username); if (jwtTokenUtil.validateToken(jwtToken, userDetails)) { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } } } filterChain.doFilter(request, response); }} 通过Postman测试登录接口获取JWT,测试成功后将令牌添加至HTTP请求头,访问受保护接口。确保权限配置正确,否则可能出现权限不足问题。
JWT在无状态应用中提供了高效的身份验证方案,结合Spring Security,可实现细粒度的权限控制。虽然面临密钥保密等挑战,但通过合理配置和定期更新,可有效保障安全性。
转载地址:http://bpoq.baihongyu.com/