Spring Security 6 JWT身份验证
·
1 .概述
- 基于JWT 实现身份认证和授权,是当前流行的一种技术解决方案。
- 使用这种方案,要求用户先发来用户名与密码,当用户名与密码校验通过时,服务器端返回一个JWT给客户端。
- 客户端拿到JWT之后,将其放到HTTP 请求名为Authorization的Header 中,随后继请求一起发给服务端
- 服务端先校验JWT的有效性,有效之后,开放资源给客户端访问。
2. Token
- Token 是一种有着特定含义的短小的字符串
- 在Web开发中,Token 通常由WebServer 端生成,“颁发”给客户端,这些Token通常保存由一些比较重要的信息,客户端在发出HTTP请求时,通常要求在HTTP Header 中放置这些Token供Server 端处理。
- 为了保证安全性,Token 通常被编码和签名,并且拥有一个特定的“生存期”,过期的Token会被废弃。
3. 实战
(1) 添加项目依赖
<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>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
(2) 定义常量
public interface SecurityConstants {
public static final String JWT_KEY="这是我的一个私有密钥,用于生成JWT";
//设置放在HTTP请求Header的名字
public static final String JWT_HEADER="Authorization";
}
(3) 生成JWT
public class JWTUtils {
public static String generateJWT(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null){
SecretKey secretKey = Keys.hmacShaKeyFor(SecurityConstants.JWT_KEY.getBytes(StandardCharsets.UTF_8));
String jwt = Jwts.builder()
.setIssuer("XZ")
.setSubject("JWT Token")
.claim("username", authentication.getName())
.claim("authorities", fillAuthorities(authentication.getAuthorities()))
.setIssuedAt(new Date())
.setExpiration(new Date(new Date().getTime()+30000000))
.signWith(secretKey)
.compact();
return jwt;
}
return "";
}
private static String fillAuthorities(Collection<? extends GrantedAuthority> collection){
Set<String> authorities = new HashSet<>();
for(GrantedAuthority authority : collection){
authorities.add(authority.getAuthority());
}
return String.join(",",authorities);
}
}
(4)在控制器中生成JWT
- 在这里,生成JWT之后,将其放到HTTP响应的body中…
@GetMapping("/token")
public ResponseEntity<String> jwtToken(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null){
return ResponseEntity.ok(JWTUtils.generateJWT());
}else {
return ResponseEntity.badRequest().body("无效的用户名与密码");
}
}
(5) 编写JWT校验代码
//使用私钥校验JWT的有效性,有效,则返回true
public static boolean validateJWT(String jwt){
try{
SecretKey secretKey = Keys.hmacShaKeyFor(SecurityConstants.JWT_KEY.getBytes(StandardCharsets.UTF_8));
//提取JWT中的信息
Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();
String username = String.valueOf(claims.get("username"));
String authorities = String.valueOf(claims.get("authorities"));
//创建Spring Security所使用的usernamePasswordAuthenticationToken
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, null, AuthorityUtils.commaSeparatedStringToAuthorityList(authorities));
//将认证结果保存到SecurityContext中
SecurityContextHolder.getContext().setAuthentication(auth);
return true;
}catch (Exception e){
return false;
}
}
(6).构建用于校验JWT的Filter
- 由于往往很多服务都需要认证与授权之后才能访问,因此,将相关逻辑放到Filter 中是一个很好的选择
public class JWTTokenValidatorFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String jwt=request.getHeader(JWT_HEADER);
if(jwt!=null){
//如果校验失败
if(!JWTUtils.validateJWT(jwt)){
// throw new BadCredentialsException("无效的Token");
System.out.println("无效的Token");
//return;
}
}
filterChain.doFilter(request, response);
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
//公开的URL,不需要校验Token
String url = request.getServletPath();
return url.startsWith("/public") || url.equals("/register") || url.equals("/token");
}
}
(7)将Filter 追加到过滤器链中

- 先从Token 中提取信息,再进行Basic 认证
@Configuration
public class MyWebSecurityFConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
//使用JWT认证,服务端无需保存会话消息
http.sessionManagement(config->{
config.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
});
http.authorizeHttpRequests(
requests->{
requests.requestMatchers("/register").permitAll();
requests.requestMatchers("/token/**").hasAnyAuthority("USER","ADMIN");
requests.requestMatchers("/public/**").hasAnyAuthority("USER","ADMIN");
requests.requestMatchers("/private/**").hasAuthority("ADMIN");
}
);
//配置cors
http.cors(AbstractHttpConfigurer::disable);
http.csrf(AbstractHttpConfigurer::disable);
http.formLogin(withDefaults());
http.httpBasic(withDefaults());
//在认证Filter完成之前,先校验Token的有效性
http.addFilterBefore(new JWTTokenValidatorFilter(), BasicAuthenticationFilter.class);
//在认证Filter完成之后使用自定义Filter 打印日志
http.addFilterAfter(new MySecurityLogFilter(), BasicAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder myPasswordEncoder() {
return new BCryptPasswordEncoder();
};
}
`
``
更多推荐
所有评论(0)