# SpringBootSecurity中JWT的使用方法 ## 一、JWT基础概念 ### 1.1 什么是JWT JSON Web Token(JWT)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息作为JSON对象。JWT由三部分组成: - Header(头部) - Payload(负载) - Signature(签名) 典型JWT格式:`xxxxx.yyyyy.zzzzz` ### 1.2 JWT的工作流程 1. 客户端通过用户名/密码登录 2. 服务端验证后生成JWT返回 3. 客户端后续请求携带JWT 4. 服务端验证JWT有效性后响应请求 ### 1.3 JWT的优势 - 无状态:服务端不需要存储会话信息 - 跨域支持:适合分布式系统和微服务架构 - 安全性:基于数字签名保证数据完整性 ## 二、Spring Security集成JWT ### 2.1 环境准备 ```xml <!-- pom.xml 依赖 --> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> </dependencies> public class JwtTokenUtil { private static final String SECRET_KEY = "your-256-bit-secret"; private static final long EXPIRATION_TIME = 864_000_000; // 10天 public static String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); return Jwts.builder() .setClaims(claims) .setSubject(userDetails.getUsername()) .setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } public static Boolean validateToken(String token, UserDetails userDetails) { final String username = extractUsername(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } private static Boolean isTokenExpired(String token) { return extractExpiration(token).before(new Date()); } public static String extractUsername(String token) { return extractClaim(token, Claims::getSubject); } private static Date extractExpiration(String token) { return extractClaim(token, Claims::getExpiration); } private static <T> T extractClaim(String token, Function<Claims, T> claimsResolver) { final Claims claims = extractAllClaims(token); return claimsResolver.apply(claims); } private static Claims extractAllClaims(String token) { return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody(); } } @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Autowired private UserDetailsService jwtUserDetailsService; @Autowired private JwtRequestFilter jwtRequestFilter; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/authenticate", "/register").permitAll() .anyRequest().authenticated() .and() .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint) .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); } } @Component public class JwtRequestFilter extends OncePerRequestFilter { @Autowired private JwtUserDetailsService jwtUserDetailsService; @Autowired private JwtTokenUtil jwtTokenUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { final String requestTokenHeader = request.getHeader("Authorization"); String username = null; String jwtToken = null; if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) { jwtToken = requestTokenHeader.substring(7); try { username = jwtTokenUtil.extractUsername(jwtToken); } catch (IllegalArgumentException e) { logger.error("Unable to get JWT Token"); } catch (ExpiredJwtException e) { logger.error("JWT Token has expired"); } } if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username); if (jwtTokenUtil.validateToken(jwtToken, userDetails)) { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); authenticationToken.setDetails( new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } } chain.doFilter(request, response); } } @RestController public class JwtAuthenticationController { @Autowired private AuthenticationManager authenticationManager; @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private JwtUserDetailsService userDetailsService; @PostMapping("/authenticate") public ResponseEntity<?> createAuthenticationToken( @RequestBody JwtRequest authenticationRequest) throws Exception { authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword()); final UserDetails userDetails = userDetailsService .loadUserByUsername(authenticationRequest.getUsername()); final String token = jwtTokenUtil.generateToken(userDetails); return ResponseEntity.ok(new JwtResponse(token)); } private void authenticate(String username, String password) throws Exception { try { authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(username, password)); } catch (DisabledException e) { throw new Exception("USER_DISABLED", e); } catch (BadCredentialsException e) { throw new Exception("INVALID_CREDENTIALS", e); } } } public class JwtRequest implements Serializable { private String username; private String password; // getters & setters } public class JwtResponse implements Serializable { private final String token; // constructor & getter } /authenticate获取tokencurl -X POST \ http://localhost:8080/authenticate \ -H 'Content-Type: application/json' \ -d '{"username":"admin","password":"password"}' curl -X GET \ http://localhost:8080/api/protected \ -H 'Authorization: Bearer <token>' @PostMapping("/refresh") public ResponseEntity<?> refreshToken(HttpServletRequest request) { String oldToken = request.getHeader("Authorization").substring(7); if (jwtTokenUtil.canTokenBeRefreshed(oldToken)) { String newToken = jwtTokenUtil.refreshToken(oldToken); return ResponseEntity.ok(new JwtResponse(newToken)); } return ResponseEntity.badRequest().body("Token cannot be refreshed"); } 实现token失效功能:
@Component public class TokenBlacklist { private Set<String> blacklist = Collections.newSetFromMap(new ConcurrentHashMap<>()); public void add(String token) { blacklist.add(token); } public boolean contains(String token) { return blacklist.contains(token); } } 在微服务架构中: 1. 使用统一的认证服务 2. 配置相同的secret key 3. 考虑使用JWT + OAuth2组合方案
本文详细介绍了在Spring Boot Security中集成JWT的完整方案,从基础概念到具体实现,涵盖了认证流程、安全配置、接口开发和进阶优化等内容。JWT为现代分布式系统提供了轻量级的安全解决方案,结合Spring Security可以构建出既安全又灵活的认证授权体系。
实际项目中,开发者还需要根据具体业务需求进行调整,比如添加多因素认证、审计日志等功能,以构建更加完善的安保系统。 “`
注:本文实际字数为约3200字,包含了完整的代码实现和理论说明。如需调整内容或字数,可以进一步修改补充。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。