JWT 的实现和与 SpringSecurity的整合
一、Token的生成与校验
1. 引入maven依赖
1 2 3 4 5
| <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.6.0</version> </dependency>
|
2. Token的生成
这里我们在测试类中进行测试
1 2 3 4 5 6 7 8 9
| @Test void jwt_generate_test() { JwtBuilder builder= Jwts.builder() .setId("888") .setSubject("丁生") .setIssuedAt(new Date()) .signWith(SignatureAlgorithm.HS256,"fabian"); System.out.println( builder.compact() ); }
|
我们就可以在控制台找到我们生成的 Token 了
1
| eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIwMDEiLCJzdWIiOiLkuIHnlJ8iLCJpYXQiOjE1OTI1NTgwODV9.VgAUtgjVW5adx85GoEcGIP6-w5VtBtYttA_zsazEO4M
|
3. Token的校验
1 2 3 4 5 6 7 8 9 10 11
| @Test void jwt_check_test() { String token="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLkuIHnlJ8iLCJpYXQiOjE1OTI1NTc3MjB9.Dn1w7uIpTZnCLF3X21bR5J3nBqKbuqSJqnA3yn6kpAQ"; Claims claims = Jwts.parser().setSigningKey("fabian").parseClaimsJws(token).getBody(); System.out.println("id:"+claims.getId()); System.out.println("subject:"+claims.getSubject()); System.out.println("IssuedAt:"+claims.getIssuedAt()); }
|
4. 异常的 Token
在解码 Token 的时候,我们会先检查签名的有效性,如果签名无效则直接抛出异常。
当我们将 Token 修改后再进行校验时,会直接告诉我们这个 Token 不能被信任

二、整合 JWT 和 SpringSecurity
1. JWT 生成和校验工具类
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
| @Component @RequiredArgsConstructor public class TokenProvider{ public String createToken(Authentication authentication) { System.out.println("开始通过授权登录信息生成token"); String authorities = authentication.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.joining(",")); return Jwts.builder() .setSubject(authentication.getName()) .claim("auth", authorities) .signWith(SignatureAlgorithm.HS512, "fabian") .compact(); } Authentication getAuthentication(String token) { System.out.println("开始从token中解析信息"); Claims claims = Jwts.parser() .setSigningKey("fabian") .parseClaimsJws(token) .getBody(); System.out.println("解析结果:"+claims.toString()); Object authoritiesStr = claims.get("auth"); Collection<? extends GrantedAuthority> authorities = !isEmpty(authoritiesStr) ? Arrays.stream(authoritiesStr.toString().split(",")) .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()) : Collections.emptyList(); User principal = new User(claims.getSubject(), "", authorities); return new UsernamePasswordAuthenticationToken(principal, token, authorities); }
}
|
2. JWT 拦截操作器
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
| @RequiredArgsConstructor public class TokenFilter extends GenericFilterBean { private final TokenProvider tokenProvider; @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("进入自定义TokenFilter拦截器=========================="); HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; String token = resolveToken(httpServletRequest); System.out.println("获取token:"+token); if(token!=null){ Authentication authentication = tokenProvider.getAuthentication(token); System.out.println("获取到的权限信息: "+authentication.getAuthorities()); } filterChain.doFilter(servletRequest, servletResponse); } private String resolveToken(HttpServletRequest request) { String bearerToken = request.getHeader("Authorization"); if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer")) { return bearerToken.replace("Bearer",""); } return null; } }
|
3. 加入过滤器链
1 2 3 4 5 6 7 8 9
| @RequiredArgsConstructor public class TokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> { private final TokenProvider tokenProvider; @Override public void configure(HttpSecurity http) { TokenFilter customFilter = new TokenFilter(tokenProvider); http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class); } }
|
4. 配置 Spring Secutity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig extends WebSecurityConfigurerAdapter { private final TokenProvider tokenProvider; @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity.authorizeRequests().antMatchers("/login").permitAll() .anyRequest().authenticated() .and() .apply(securityConfigurerAdapter()); } private TokenConfigurer securityConfigurerAdapter() { return new TokenConfigurer(tokenProvider); } }
|
5. 准备基本业务操作用户类
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
| @Repository public class UserInfo { private String username; private String password; private String role; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Service public class UserInfoService { public UserInfo getUserInfo(String username){ if (username.equals("admin")){ UserInfo userInfo = new UserInfo(); userInfo.setUsername("admin"); userInfo.setPassword("admin"); userInfo.setRole("admin"); return userInfo; } return null; } }
|
6. 接管 UserDetailsService
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
| @Component public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserInfoService userInfoService; @Autowired private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { UserInfo userInfo = userInfoService.getUserInfo(username); if (userInfo == null) { throw new UsernameNotFoundException("用户不存在"); } String role = userInfo.getRole(); List<GrantedAuthority> authorities = new ArrayList<>(); authorities.add(new SimpleGrantedAuthority(role)); return new User( userInfo.getUsername(), passwordEncoder.encode(userInfo.getPassword()), authorities ); } }
|
7. 对外接口
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
| @RestController @RequiredArgsConstructor public class HelloController { private final TokenProvider tokenProvider; private final CustomUserDetailsService customUserDetailsService; private final AuthenticationManagerBuilder authenticationManagerBuilder ; @GetMapping("/hello") public String hello() { return "hello"; } @GetMapping("/login") public String login() throws Exception { UserDetails user = customUserDetailsService.loadUserByUsername("admin"); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), "admin"); Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken); SecurityContextHolder.getContext().setAuthentication(authentication); String token = tokenProvider.createToken(authentication); System.out.println("生成的令牌:"+token); return token; } }
|
三、 接口测试
这里我们是要 postman 来作为我们的接口测试工具
测试 http://localhost:8080/login

控制台打印

测试 http://localhost:8080/hello
将token加入请求头

控制台打印
