关于JWT是什么就不赘述了,可以看看阮一峰大佬的文章:https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
定义JWT Body
首先需要定义一个Bean用来存放JWT Body,字段可以根据具体业务决定。这一步当然可以跳过,你可以用Map传值,只不过可维护性会差点
1 2 3 4 5 6 7 8
| @Data public class JwtBody {
private String id; private String user; private String role;
}
|
编写JWT工具类
首先添加Maven依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.2</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency>
|
工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public abstract class JwtUtil { @SuppressWarnings("unchecked") public static String generate(JwtBody claims, String secret) { final SecretKey secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); return Jwts.builder() .setExpiration(Date.from(ZonedDateTime.now().plusHours(2).toInstant())) .addClaims(BeanMap.create(claims)) .signWith(secretKey).compact(); } public static JwtBody parse(String token, String secret) { final SecretKey secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); final JwtBody claims = new JwtBody(); final Claims body = Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token).getBody(); BeanMap.create(claims).putAll(body); return claims; }
}
|
两个方法,一个编码,一个解码
编写Login Controller
编写一个登录API接口,用来生成Token返回给客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @RestController @RequiredArgsConstructor @RequestMapping("") @CrossOrigin public class AuthController {
private final AuthService authService;
@PostMapping("/login") public UserLoginResponse login(@RequestBody UserLoginRequest userLoginRequest) { String token = authService.login(userLoginRequest.getUsername(), userLoginRequest.getPassword()); return UserLoginResponse.builder().token(token).build(); }
}
|
登录方法的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public String login(String username, String password) { User user = userService.getByUsername(username); if (!passwordEncoder.matches(password, user.getPassword())) { throw new BadCredentialsException("Password error"); }
JwtBody claims = new JwtBody() { { setId(user.getId().toString()); setUser(user.getUsername()); setRole(user.getRole().name()); } }; return JwtUtil.generate(claims, config.getJwtSecret()) }
|
编写JWT Filter
需要编写一个过滤器,添加到Spring Security过滤器链,用来验证Token授权
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
| @Component public class JwtFilter extends OncePerRequestFilter {
private final static String TOKEN_PREFIX = "Bearer"; private AuthenticationManager authenticationManager; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { final String header = request.getHeader(HttpHeaders.AUTHORIZATION); if (header != null && header.startsWith(TOKEN_PREFIX)) { final String token = header.replaceAll("^" + TOKEN_PREFIX, "").trim(); Authentication auth = new JwtAuthentication(token); try { Authentication authResult = authenticationManager.authenticate(auth); SecurityContextHolder.getContext().setAuthentication(authResult); } catch (JwtException e) { logger.warn("Authentication failed: " + e.getLocalizedMessage()); } } filterChain.doFilter(request, response); }
@Autowired public void setAuthenticationManager(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; }
}
|
JwtAuthentication实现
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 48 49
| public class JwtAuthentication implements Authentication {
@Getter private final String token; @Getter @Setter private User user; private Boolean isAuthenticated = false;
public JwtAuthentication(String token) { this.token = token; }
@Override public Collection<? extends GrantedAuthority> getAuthorities() { return user.getAuthorities(); }
@Override public Object getCredentials() { return null; }
@Override public Object getDetails() { return user; }
@Override public Object getPrincipal() { return user.getUsername(); }
@Override public boolean isAuthenticated() { return isAuthenticated; }
@Override public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { this.isAuthenticated = isAuthenticated; }
@Override public String getName() { return getPrincipal().toString(); }
}
|
编写JwtAuthenticationProvider
这个类用来实现具体的Token验证操作
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
| @Component @RequiredArgsConstructor public class JwtAuthenticationProvider implements AuthenticationProvider {
private final Config config;
@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { JwtAuthentication jwtAuthentication = (JwtAuthentication) authentication; JwtBody claims; try { claims = JwtUtil.parse(jwtAuthentication.getToken(), config.getJwtSecret()); } catch (SignatureException e) { throw new AccessDeniedException(e.getLocalizedMessage()); } final User user = new User(); user.setId(Long.valueOf(claims.getId().toString())); user.setUsername(claims.getUser()); user.setRole(Role.valueOf(claims.getRole())); jwtAuthentication.setUser(user); jwtAuthentication.setAuthenticated(true); return jwtAuthentication; }
@Override public boolean supports(Class<?> authentication) { return JwtAuthentication.class.isAssignableFrom(authentication); }
}
|
编写Spring Security配置类
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
| @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {
private JwtFilter jwtFilter; private JwtAuthenticationProvider jwtAuthenticationProvider;
@Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); }
@Override protected void configure(AuthenticationManagerBuilder auth) { auth.authenticationProvider(jwtAuthenticationProvider); }
@Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
@Override protected void configure(HttpSecurity http) throws Exception { http.cors().and().csrf().disable().logout().disable().formLogin().disable() .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeRequests(request -> { request.antMatchers("/api/users/*").hasAuthority(Role.ADMIN.name()); request.antMatchers("/login", "/register").permitAll(); }) .addFilterAt(jwtFilter, UsernamePasswordAuthenticationFilter.class); } @Autowired public void setJwtFilter(JwtFilter jwtFilter) { this.jwtFilter = jwtFilter; }
@Autowired public void setJwtAuthenticationProvider(JwtAuthenticationProvider jwtAuthenticationProvider) { this.jwtAuthenticationProvider = jwtAuthenticationProvider; }
}
|