怎么在Spring-boot中利用Shrio实现JWT验证

今天就跟大家聊聊有关怎么在Spring-boot中利用Shrio实现JWT验证,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

创新互联拥有一支富有激情的企业网站制作团队,在互联网网站建设行业深耕10年,专业且经验丰富。10年网站优化营销经验,我们已为1000+中小企业提供了成都做网站、成都网站制作、成都外贸网站建设解决方案,按需开发,设计满意,售后服务无忧。所有客户皆提供一年免费网站维护!

pom.mx引入相关jar包

 
  
    org.apache.shiro
    shiro-spring
    ${shiro.version}
  
  
    org.apache.shiro
    shiro-core
    ${shiro.version}
  
 
   
    io.jsonwebtoken
    jjwt
    0.9.0
  

Shrio 的相关配置

划重点!!自定义了一个Filter

filterMap.put("JWTFilter", new JWTFilter());
@Configuration
public class ShiroConfig {
  @Bean
  public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(securityManager);
    // 添加自己的过滤器并且取名为JWTFilter
    Map filterMap = new HashMap<>();
    filterMap.put("JWTFilter", new JWTFilter());
    shiroFilterFactoryBean.setFilters(filterMap);
    /*
     * 自定义url规则
     * http://shiro.apache.org/web.html#urls-
     */
    Map filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap();
    filterChainDefinitionMap.put("/**", "JWTFilter");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    return shiroFilterFactoryBean;
  }


  /**
   * securityManager 不用直接注入shiroDBRealm,可能会导致事务失效
   * 解决方法见 handleContextRefresh
   * http://www.debugrun.com/a/NKS9EJQ.html
   */
  @Bean("securityManager")
  public DefaultWebSecurityManager securityManager(TokenRealm tokenRealm) {
    DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
    manager.setRealm(tokenRealm);
    /*
     * 关闭shiro自带的session,详情见文档
     * http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29
     */
    DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
    DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
    defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
    subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
    manager.setSubjectDAO(subjectDAO);
    return manager;
  }

  @Bean
  public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
    return new LifecycleBeanPostProcessor();
  }

  @Bean(name = "TokenRealm")
  @DependsOn("lifecycleBeanPostProcessor")
  public TokenRealm tokenRealm() {
    return new TokenRealm();
  }

  @Bean
  @DependsOn("lifecycleBeanPostProcessor")
  public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
    // 强制使用cglib,防止重复代理和可能引起代理出错的问题
    // https://zhuanlan.zhihu.com/p/29161098
    defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
    return defaultAdvisorAutoProxyCreator;
  }

  @Bean
  public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(SecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
    return new AuthorizationAttributeSourceAdvisor();
  }
}

自定义Shrio filter

执行顺序:preHandle -> doFilterInternal -> executeLogin -> onLoginSuccess

主要判断是不是登录请求的是 doFilterInternal

public class JWTFilter extends BasicHttpAuthenticationFilter {
  /**
   * 自定义执行登录的方法
   */
  @Override
  protected boolean executeLogin(ServletRequest request, ServletResponse response) throws IOException {
    HttpServletRequest httpServletRequest = (HttpServletRequest) request;
    UsernamePasswordToken usernamePasswordToken = JSON.parseObject(httpServletRequest.getInputStream(), UsernamePasswordToken.class);
    // 提交给realm进行登入,如果错误他会抛出异常并被捕获
    Subject subject = this.getSubject(request, response);
    subject.login(usernamePasswordToken);
    return this.onLoginSuccess(usernamePasswordToken, subject, request, response);
    //错误抛出异常
  }

  /**
   * 最先执行的方法
   */
  @Override
  protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
    return super.preHandle(request, response);
  }

  /**
   * 登录成功后登录的操作
   * 加上jwt 的header
   */
  @Override
  protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) {
    HttpServletResponse httpServletResponse = (HttpServletResponse) response;
    String jwtToken = Jwts.builder()
        .setId(token.getPrincipal().toString())
        .setExpiration(DateTime.now().plusMinutes(30).toDate())
        .signWith(SignatureAlgorithm.HS256, JWTCost.signatureKey)
        .compact();
    httpServletResponse.addHeader(AUTHORIZATION_HEADER, jwtToken);
    return true;
  }

  /**
   * 登录以及校验的主要流程
   * 判断是否是登录,或者是登陆后普通的一次请求
   */
  @Override
  public void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
    HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
    String servletPath = httpServletRequest.getServletPath();
    if (StringUtils.equals(servletPath, "/login")) {
      //执行登录
      this.executeLogin(servletRequest, servletResponse);
    } else {
      String authenticationHeader = httpServletRequest.getHeader(AUTHORIZATION_HEADER);
      if (StringUtils.isNotEmpty(authenticationHeader)) {

        Claims body = Jwts.parser()
            .setSigningKey(JWTCost.signatureKey)
            .parseClaimsJws(authenticationHeader)
            .getBody();
        if (body != null) {
          //更新token
          body.setExpiration(DateTime.now().plusMinutes(30).toDate());
          String updateToken = Jwts.builder().setClaims(body).compact();
          httpServletResponse.addHeader(AUTHORIZATION_HEADER, updateToken);

          //添加用户凭证
          PrincipalCollection principals = new SimplePrincipalCollection(body.getId(), JWTCost.UserNamePasswordRealm);//拼装shiro用户信息
          WebSubject.Builder builder = new WebSubject.Builder(servletRequest, servletResponse);
          builder.principals(principals);
          builder.authenticated(true);
          builder.sessionCreationEnabled(false);
          WebSubject subject = builder.buildWebSubject();
          //塞入容器,统一调用
          ThreadContext.bind(subject);
          filterChain.doFilter(httpServletRequest, httpServletResponse);
        }
      } else {
        httpServletResponse.setStatus(HttpStatus.FORBIDDEN.value());
      }
    }
  }
}

登录失败处理

处理Shrio异常

@RestControllerAdvice
public class GlobalControllerExceptionHandler {
  @ExceptionHandler(value = Exception.class)
  public Object allExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) {
    String message = exception.getCause().getMessage();
    LogUtil.error(message);
    return new ResultInfo(exception.getClass().getName(), message);
  }

  /*=========== Shiro 异常拦截==============*/

  @ExceptionHandler(value = IncorrectCredentialsException.class)
  public String IncorrectCredentialsException(HttpServletRequest request, HttpServletResponse response, Exception exception) {
    response.setStatus(HttpStatus.FORBIDDEN.value());
    return "IncorrectCredentialsException";
  }

  @ExceptionHandler(value = UnknownAccountException.class)
  public String UnknownAccountException(HttpServletRequest request, HttpServletResponse response, Exception exception) {
    response.setStatus(HttpStatus.FORBIDDEN.value());
    return "UnknownAccountException";
  }

  @ExceptionHandler(value = LockedAccountException.class)
  public String LockedAccountException(HttpServletRequest request, HttpServletResponse response, Exception exception) {
    response.setStatus(HttpStatus.FORBIDDEN.value());
    return "LockedAccountException";
  }

  @ExceptionHandler(value = ExcessiveAttemptsException.class)
  public String ExcessiveAttemptsException(HttpServletRequest request, HttpServletResponse response, Exception exception) {
    response.setStatus(HttpStatus.FORBIDDEN.value());
    return "ExcessiveAttemptsException";
  }

  @ExceptionHandler(value = AuthenticationException.class)
  public String AuthenticationException(HttpServletRequest request, HttpServletResponse response, Exception exception) {
    response.setStatus(HttpStatus.FORBIDDEN.value());
    return "AuthenticationException";
  }

  @ExceptionHandler(value = UnauthorizedException.class)
  public String UnauthorizedException(HttpServletRequest request, HttpServletResponse response, Exception exception) {
    response.setStatus(HttpStatus.FORBIDDEN.value());
    return "UnauthorizedException";
  }
}

处理JWT异常

这是个坑,因为是在filter内发生的异常,@ExceptionHandler是截获不到的。

/**
 * 截获spring boot Error页面
 */
@RestController
public class GlobalExceptionHandler implements ErrorController {
  @Override
  public String getErrorPath() {
    return "/error";
  }

  @RequestMapping(value = "/error")
  public Object error(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 错误处理逻辑
    Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception");
    Throwable cause = exception.getCause();
    if (cause instanceof ExpiredJwtException) {
      response.setStatus(HttpStatus.GATEWAY_TIMEOUT.value());
      return new ResultInfo("ExpiredJwtException", cause.getMessage());
    }
    if (cause instanceof MalformedJwtException) {
      response.setStatus(HttpStatus.FORBIDDEN.value());
      return new ResultInfo("MalformedJwtException", cause.getMessage());
    }
    return new ResultInfo(cause.getCause().getMessage(), cause.getMessage());
  }
}

springboot是什么

springboot一种全新的编程规范,其设计目的是用来简化新Spring应用的初始搭建以及开发过程,SpringBoot也是一个服务于框架的框架,服务范围是简化配置文件。

看完上述内容,你们对怎么在Spring-boot中利用Shrio实现JWT验证有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注创新互联行业资讯频道,感谢大家的支持。


本文题目:怎么在Spring-boot中利用Shrio实现JWT验证
文章路径:http://scyanting.com/article/pejhpc.html