欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring-boot結合Shrio實現(xiàn)JWT的方法

 更新時間:2018年05月07日 08:34:39   作者:workabee  
這篇文章主要介紹了Spring-boot結合Shrio實現(xiàn)JWT的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

本文介紹了Spring-boot結合Shrio實現(xiàn)JWT的方法,分享給大家,具體如下:

關于驗證大致分為兩個方面:

  1. 用戶登錄時的驗證;
  2. 用戶登錄后每次訪問時的權限認證

主要解決方法:使用自定義的Shiro Filter

項目搭建:

這是一個spring-boot 的web項目,不了解spring-boot的項目搭建,請google。

pom.mx引入相關jar包

 <!-- shiro 權限管理 -->
  <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>${shiro.version}</version>
  </dependency>
  <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>${shiro.version}</version>
  </dependency>
 <!-- JWT -->
   <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
  </dependency>

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<String, Filter> filterMap = new HashMap<>();
    filterMap.put("JWTFilter", new JWTFilter());
    shiroFilterFactoryBean.setFilters(filterMap);
    /*
     * 自定義url規(guī)則
     * http://shiro.apache.org/web.html#urls-
     */
    Map<String, String> 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

執(zhí)行順序:preHandle -> doFilterInternal -> executeLogin -> onLoginSuccess

主要判斷是不是登錄請求的是 doFilterInternal

public class JWTFilter extends BasicHttpAuthenticationFilter {
  /**
   * 自定義執(zhí)行登錄的方法
   */
  @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);
    //錯誤拋出異常
  }

  /**
   * 最先執(zhí)行的方法
   */
  @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")) {
      //執(zhí)行登錄
      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();
          //塞入容器,統(tǒng)一調(diào)用
          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內(nèi)發(fā)生的異常,@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());
  }
}

關于權限等授權信息,可以直接放到Redis中實現(xiàn)緩存。我認為也是不錯的。

源碼奉上:githup-shiro分支 :溫馨提示:平時測試代碼可能比較亂。

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • Java NIO.2 使用Path接口來監(jiān)聽文件、文件夾變化

    Java NIO.2 使用Path接口來監(jiān)聽文件、文件夾變化

    Java7對NIO進行了大的改進,新增了許多功能,接下來通過本文給大家介紹Java NIO.2 使用Path接口來監(jiān)聽文件、文件夾變化 ,需要的朋友可以參考下
    2019-05-05
  • Java異常處理操作 Throwable、Exception、Error

    Java異常處理操作 Throwable、Exception、Error

    這篇文章主要介紹了Java異常處理操作 Throwable、Exception、Error,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java實現(xiàn)讀取resources目錄下的文件路徑的九種方式

    Java實現(xiàn)讀取resources目錄下的文件路徑的九種方式

    本文主要介紹了Java實現(xiàn)讀取resources目錄下的文件路徑的九種方式,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-04-04
  • Java必須掌握的 4 大基礎

    Java必須掌握的 4 大基礎

    我們一直在學習Java,但Java中總有一些概念含混不清,不論是對初級還是高級程序員都是如此。所以,這篇文章的目的就是弄清楚這些概念,需要的小伙伴可以參考以下喲
    2021-09-09
  • SpringBoot@Componet注解注入失敗的問題

    SpringBoot@Componet注解注入失敗的問題

    這篇文章主要介紹了SpringBoot@Componet注解注入失敗的問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • java數(shù)據(jù)結構與算法之桶排序?qū)崿F(xiàn)方法詳解

    java數(shù)據(jù)結構與算法之桶排序?qū)崿F(xiàn)方法詳解

    這篇文章主要介紹了java數(shù)據(jù)結構與算法之桶排序?qū)崿F(xiàn)方法,結合具體實例形式詳細分析了桶排序的概念、原理、實現(xiàn)方法與相關操作技巧,需要的朋友可以參考下
    2017-05-05
  • spring中bean id相同引發(fā)故障的分析與解決

    spring中bean id相同引發(fā)故障的分析與解決

    最近在工作中遇到了關于bean id相同引發(fā)故障的問題,通過查找相關資料終于解決了,下面這篇文章主要給大家介紹了因為spring中bean id相同引發(fā)故障的分析與解決方法,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-09-09
  • Java OSS批量下載并壓縮為ZIP代碼實例

    Java OSS批量下載并壓縮為ZIP代碼實例

    這篇文章主要介紹了Java OSS批量下載并壓縮為ZIP代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-09-09
  • Java中的ReentrantLock、ReentrantReadWriteLock、StampedLock詳解

    Java中的ReentrantLock、ReentrantReadWriteLock、StampedLock詳解

    這篇文章主要介紹了Java中的ReentrantLock、ReentrantReadWriteLock、StampedLock詳解,讀寫鎖:一個資源能夠被多個讀線程訪問,或者被一個寫線程訪問但是不能同時存在讀寫線程,需要的朋友可以參考下
    2024-01-01
  • SpringBoot攔截器以及源碼詳析

    SpringBoot攔截器以及源碼詳析

    攔截器在我們平時的項目中用處有很多,如:日志記錄(我們后續(xù)章節(jié)會講到)、用戶登錄狀態(tài)攔截、安全攔截等等,所以下面這篇文章主要給大家介紹了關于SpringBoot攔截器以及源碼的相關資料,需要的朋友可以參考下
    2021-07-07

最新評論