Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

SpringSecurity整合JWT

Exrick edited this page Apr 13, 2019 · 2 revisions

JWT

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的 官网:https://jwt.io

  • JSON Web Token由Header、Payload、Signature三部分组成,它们之间用圆点(.)连接。 一个典型的JWT看起来是这个样子的: xxxxx.yyyyy.zzzzz 分别对应:Header.Payload.Signature

Header

header典型的由两部分组成:token的类型("JWT")和算法名称(比如:HMAC SHA256或者RSA等等)。

例如:

{ "alg": "HS256", "typ": "JWT" }

用Base64对这个JSON编码就得到JWT的第一部分

Payload

JWT的第二部分是payload,通常在这部分存入交互信息,XBoot中存入了用户名和用户权限(避免每次请求再次读取用户权限)以及token失效时间。 例如:

{ "sub": "admin", "authorities": "["添加用户","ROLE_ADMIN"]", "exp": 1555554537 } 对payload进行Base64编码就得到JWT的第二部分

注意:签名并不是加密,任何人都能看到JWT里的内容,除非它们是加密的,因此请勿放置明文敏感信息至JWT中

Signature

签名是用于验证消息在传递过程中有没有被更改,并且对于使用私钥签名的token,它还可以验证JWT的发送方是否为它所称的发送方。 例如: HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

JWT缺点:JWT是无法撤销的,除非是达到了设定的过期时间,且刷新Token机制麻烦。解决放案:XBoot配置使用Redis记录,30分钟内无请求自动失效,可随时管理token,详见代码

Spring Security整合

  • 添加依赖
<!-- Spring Security -->
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JWT -->
<dependency>
 <groupId>io.jsonwebtoken</groupId>
 <artifactId>jjwt</artifactId>
 <version>0.9.1</version>
</dependency>
  • Spring Security核心配置入口
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 ...
 @Autowired
 private AuthenticationSuccessHandler successHandler;
 @Autowired
 private AuthenticationFailHandler failHandler;
 @Autowired
 private RestAccessDeniedHandler accessDeniedHandler;
 @Override
 protected void configure(HttpSecurity http) throws Exception {
 ...
 registry.and()
 ...
 //成功处理类
 .successHandler(successHandler)
 //失败
 .failureHandler(failHandler)
 //关闭跨站请求防护
 .csrf().disable()
 //前后端分离采用JWT 不需要session
 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
 .and()
 //自定义权限拒绝处理类
 .exceptionHandling().accessDeniedHandler(accessDeniedHandler)
 .and()
 //添加JWT过滤器 除已配置的其它请求都需经过此过滤器
 .addFilter(new JWTAuthenticationFilter(authenticationManager()));
 }
}
  • 成功处理类
@Slf4j
@Component
public class AuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
 @Value("${xboot.tokenExpireTime}")
 private Integer tokenExpireTime;
 @Value("${xboot.saveLoginTime}")
 private Integer saveLoginTime;
 @Override
 @SystemLog(description = "登录系统", type = LogType.LOGIN)
 public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
 //用户选择保存登录状态几天
 String saveLogin = request.getParameter(SecurityConstant.SAVE_LOGIN);
 ...
 String username = ((UserDetails)authentication.getPrincipal()).getUsername();
 List<GrantedAuthority> authorities = (List<GrantedAuthority>) ((UserDetails)authentication.getPrincipal()).getAuthorities();
 List<String> list = new ArrayList<>();
 for(GrantedAuthority g : authorities){
 list.add(g.getAuthority());
 }
 // 登陆成功生成token
 String token = SecurityConstant.TOKEN_SPLIT + Jwts.builder()
 //主题 放入用户名
 .setSubject(username)
 //自定义属性 放入用户拥有请求权限
 .claim(SecurityConstant.AUTHORITIES, new Gson().toJson(list))
 //失效时间
 .setExpiration(new Date(System.currentTimeMillis() + tokenExpireTime * 60 * 1000))
 //签名算法和密钥
 .signWith(SignatureAlgorithm.HS512, SecurityConstant.JWT_SIGN_KEY)
 .compact();
 ResponseUtil.out(response, ResponseUtil.resultMap(true,200,"登录成功", token));
 }
}
  • 失败处理类
@Slf4j
@Component
public class AuthenticationFailHandler extends SimpleUrlAuthenticationFailureHandler {
 ...
 @Override
 public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
 ...
 if (e instanceof UsernameNotFoundException || e instanceof BadCredentialsException) {
 ResponseUtil.out(response, ResponseUtil.resultMap(false,500,"用户名或密码错误"));
 } else if (e instanceof DisabledException) {
 ResponseUtil.out(response, ResponseUtil.resultMap(false,500,"账户被禁用,请联系管理员"));
 } else if (e instanceof LoginFailLimitException){
 ResponseUtil.out(response, ResponseUtil.resultMap(false,500,((LoginFailLimitException) e).getMsg()));
 } else {
 ResponseUtil.out(response, ResponseUtil.resultMap(false,500,"登录失败,其他内部错误"));
 }
 }
}
  • 自定义权限拒绝处理类
@Component
public class RestAccessDeniedHandler implements AccessDeniedHandler {
 @Override
 public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)
 throws IOException, ServletException {
 ResponseUtil.out(response, ResponseUtil.resultMap(false,403,"抱歉,您没有访问权限"));
 }
}

Clone this wiki locally

AltStyle によって変換されたページ (->オリジナル) /