1. 程式人生 > 其它 >Spring Boot整合Shiro 許可權管理 在前後端分離的SpringBoot專案中整合Shiro許可權框架

Spring Boot整合Shiro 許可權管理 在前後端分離的SpringBoot專案中整合Shiro許可權框架

Spring Boot整合Shiro 許可權管理 在前後端分離的SpringBoot專案中整合Shiro許可權框架

Spring Boot整合Shiro 許可權管理 在前後端分離的SpringBoot專案中整合Shiro許可權框架

專案背景

公司在幾年前就採用了前後端分離的開發模式,前端所有請求都使用ajax。這樣的專案結構在與CAS單點登入等許可權管理框架整合時遇到了很多問題,使得許可權部分的程式碼冗長醜陋,CAS的各種重定向也使得使用者體驗很差,在前端使用vue-router管理頁面跳轉時,問題更加尖銳。於是我就在尋找一個解決方案,這個方案應該對程式碼的侵入較少,開發速度快,實現優雅。最近無意中看到springboot與shiro框架整合的文章,在瞭解了springboot以及shiro的發展狀況,並學習了使用方法後,開始在網上搜索前後端分離模式下這兩個框架的適應性,在經過測試後發現可行,完全符合個人預期。

解決方案

本文中專案核心包為SpringBoot1.5.9.RELEASE以及shiro-spring 1.4.0,為了加快開發效率,持久化框架使用hibernate-JPA,為增加可靠性,sessionId的管理使用了shiro-redis開源外掛,避免sessionId斷電丟失,同時使得多端可共享session,專案結構為多模組專案,詳見下圖。

其中spring-boot-shiro模組為本文重點,該模組包含shiro核心配置,shiro資料來源配置以及各種自定義實現,登入相關服務等。該模組在專案中使用時可直接在pom中引用,並在spring-boot-main入口模組中配置相應資料庫連線資訊即可,且該模組可以在多個專案中複用,避免重複開發。spring-boot-module1為模擬真實專案中的業務模組,可能會有多個。spring-boot-common中包含通用工具類,常量,異常等等。多模組專案的搭建在本文中不作贅述。

母模組pom.xml程式碼如下

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion
    >
    4.0.0</modelVersion>
  6. <groupId>com.xxx</groupId>
  7. <artifactId>spring-boot-parent</artifactId>
  8. <packaging>pom</packaging>
  9. <version>1.0-SNAPSHOT</version>
  10. <modules>
  11. <module>spring-boot-main</module>
  12. <module>spring-boot-module1</module>
  13. <module>spring-boot-shiro</module>
  14. <module>spring-boot-common</module>
  15. </modules>
  16. <properties>
  17. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  18. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  19. <java.version>1.8</java.version>
  20. <spring-boot.version>1.5.9.RELEASE</spring-boot.version>
  21. <shiro.version>1.4.0</shiro.version>
  22. </properties>
  23. <dependencies>
  24. <dependency>
  25. <groupId>org.springframework.boot</groupId>
  26. <artifactId>spring-boot-starter-web</artifactId>
  27. <version>${spring-boot.version}</version>
  28. </dependency>
  29. <!--在外部tomcat中釋出故移除內建包-->
  30. <dependency>
  31. <groupId>org.springframework.boot</groupId>
  32. <artifactId>spring-boot-starter-tomcat</artifactId>
  33. <version>${spring-boot.version}</version>
  34. <scope>provided</scope>
  35. </dependency>
  36. <dependency>
  37. <groupId>org.springframework.boot</groupId>
  38. <artifactId>spring-boot-starter-test</artifactId>
  39. <version>${spring-boot.version}</version>
  40. <scope>test</scope>
  41. </dependency>
  42. <dependency>
  43. <groupId>org.springframework.boot</groupId>
  44. <artifactId>spring-boot-devtools</artifactId>
  45. <version>${spring-boot.version}</version>
  46. <optional>true</optional>
  47. </dependency>
  48. <dependency>
  49. <groupId>org.springframework.boot</groupId>
  50. <artifactId>spring-boot-starter-data-jpa</artifactId>
  51. <version>${spring-boot.version}</version>
  52. </dependency>
  53. <dependency>
  54. <groupId>org.apache.shiro</groupId>
  55. <artifactId>shiro-spring</artifactId>
  56. <version>${shiro.version}</version>
  57. </dependency>
  58. <dependency>
  59. <groupId>com.alibaba</groupId>
  60. <artifactId>fastjson</artifactId>
  61. <version>1.2.8</version>
  62. </dependency>
  63. <dependency>
  64. <groupId>com.alibaba</groupId>
  65. <artifactId>druid</artifactId>
  66. <version>1.0.28</version>
  67. </dependency>
  68. <dependency>
  69. <groupId>mysql</groupId>
  70. <artifactId>mysql-connector-java</artifactId>
  71. <version>5.1.39</version>
  72. <scope>runtime</scope>
  73. </dependency>
  74. <!--<dependency>-->
  75. <!--<groupId>org.springframework.boot</groupId>-->
  76. <!--<artifactId>spring-boot-starter-thymeleaf</artifactId>-->
  77. <!--<version>${spring-boot.version}</version>-->
  78. <!--</dependency>-->
  79. <!--<dependency>-->
  80. <!--<groupId>net.sourceforge.nekohtml</groupId>-->
  81. <!--<artifactId>nekohtml</artifactId>-->
  82. <!--<version>1.9.22</version>-->
  83. <!--</dependency>-->
  84. </dependencies>
  85. </project>

spring-boot-shiro模組介面如下圖

傳統結構專案中,shiro從cookie中讀取sessionId以此來維持會話,在前後端分離的專案中(也可在移動APP專案使用),我們選擇在ajax的請求頭中傳遞sessionId,因此需要重寫shiro獲取sessionId的方式。自定義MySessionManager類繼承DefaultWebSessionManager類,重寫getSessionId方法,程式碼如下

  1. import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
  2. import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
  3. import org.apache.shiro.web.util.WebUtils;
  4. import org.springframework.util.StringUtils;
  5. import javax.servlet.ServletRequest;
  6. import javax.servlet.ServletResponse;
  7. import java.io.Serializable;
  8. /**
  9. * Created by Administrator on 2017/12/11.
  10. * 自定義sessionId獲取
  11. */
  12. public class MySessionManager extends DefaultWebSessionManager {
  13. private static final String AUTHORIZATION = "Authorization";
  14. private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
  15. public MySessionManager() {
  16. super();
  17. }
  18. @Override
  19. protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
  20. String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
  21. //如果請求頭中有 Authorization 則其值為sessionId
  22. if (!StringUtils.isEmpty(id)) {
  23. request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
  24. request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
  25. request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
  26. return id;
  27. } else {
  28. //否則按預設規則從cookie取sessionId
  29. return super.getSessionId(request, response);
  30. }
  31. }
  32. }

如何配置讓shiro執行我們的自定義sessionManager呢?下面看ShiroConfig類。

  1. package com.xxx.shiro.config;
  2. import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
  3. import org.apache.shiro.mgt.SecurityManager;
  4. import org.apache.shiro.session.mgt.SessionManager;
  5. import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
  6. import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
  7. import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
  8. import org.crazycake.shiro.RedisCacheManager;
  9. import org.crazycake.shiro.RedisManager;
  10. import org.crazycake.shiro.RedisSessionDAO;
  11. import org.springframework.beans.factory.annotation.Value;
  12. import org.springframework.context.annotation.Bean;
  13. import org.springframework.context.annotation.Configuration;
  14. import org.springframework.web.servlet.HandlerExceptionResolver;
  15. import java.util.LinkedHashMap;
  16. import java.util.Map;
  17. /**
  18. * Created by Administrator on 2017/12/11.
  19. */
  20. @Configuration
  21. public class ShiroConfig {
  22. @Value("${spring.redis.shiro.host}")
  23. private String host;
  24. @Value("${spring.redis.shiro.port}")
  25. private int port;
  26. @Value("${spring.redis.shiro.timeout}")
  27. private int timeout;
  28. @Value("${spring.redis.shiro.password}")
  29. private String password;
  30. @Bean
  31. public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
  32. System.out.println("ShiroConfiguration.shirFilter()");
  33. ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
  34. shiroFilterFactoryBean.setSecurityManager(securityManager);
  35. Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
  36. //注意過濾器配置順序 不能顛倒
  37. //配置退出 過濾器,其中的具體的退出程式碼Shiro已經替我們實現了,登出後跳轉配置的loginUrl
  38. filterChainDefinitionMap.put("/logout", "logout");
  39. // 配置不會被攔截的連結 順序判斷
  40. filterChainDefinitionMap.put("/static/**", "anon");
  41. filterChainDefinitionMap.put("/ajaxLogin", "anon");
  42. filterChainDefinitionMap.put("/login", "anon");
  43. filterChainDefinitionMap.put("/**", "authc");
  44. //配置shiro預設登入介面地址,前後端分離中登入介面跳轉應由前端路由控制,後臺僅返回json資料
  45. shiroFilterFactoryBean.setLoginUrl("/unauth");
  46. // 登入成功後要跳轉的連結
  47. // shiroFilterFactoryBean.setSuccessUrl("/index");
  48. //未授權介面;
  49. // shiroFilterFactoryBean.setUnauthorizedUrl("/403");
  50. shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
  51. return shiroFilterFactoryBean;
  52. }
  53. /**
  54. * 憑證匹配器
  55. * (由於我們的密碼校驗交給Shiro的SimpleAuthenticationInfo進行處理了
  56. * )
  57. *
  58. * @return
  59. */
  60. @Bean
  61. public HashedCredentialsMatcher hashedCredentialsMatcher() {
  62. HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
  63. hashedCredentialsMatcher.setHashAlgorithmName("md5");//雜湊演算法:這裡使用MD5演算法;
  64. hashedCredentialsMatcher.setHashIterations(2);//雜湊的次數,比如雜湊兩次,相當於 md5(md5(""));
  65. return hashedCredentialsMatcher;
  66. }
  67. @Bean
  68. public MyShiroRealm myShiroRealm() {
  69. MyShiroRealm myShiroRealm = new MyShiroRealm();
  70. myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
  71. return myShiroRealm;
  72. }
  73. @Bean
  74. public SecurityManager securityManager() {
  75. DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
  76. securityManager.setRealm(myShiroRealm());
  77. // 自定義session管理 使用redis
  78. securityManager.setSessionManager(sessionManager());
  79. // 自定義快取實現 使用redis
  80. securityManager.setCacheManager(cacheManager());
  81. return securityManager;
  82. }
  83. //自定義sessionManager
  84. @Bean
  85. public SessionManager sessionManager() {
  86. MySessionManager mySessionManager = new MySessionManager();
  87. mySessionManager.setSessionDAO(redisSessionDAO());
  88. return mySessionManager;
  89. }
  90. /**
  91. * 配置shiro redisManager
  92. * <p>
  93. * 使用的是shiro-redis開源外掛
  94. *
  95. * @return
  96. */
  97. public RedisManager redisManager() {
  98. RedisManager redisManager = new RedisManager();
  99. redisManager.setHost(host);
  100. redisManager.setPort(port);
  101. redisManager.setExpire(1800);// 配置快取過期時間
  102. redisManager.setTimeout(timeout);
  103. redisManager.setPassword(password);
  104. return redisManager;
  105. }
  106. /**
  107. * cacheManager 快取 redis實現
  108. * <p>
  109. * 使用的是shiro-redis開源外掛
  110. *
  111. * @return
  112. */
  113. @Bean
  114. public RedisCacheManager cacheManager() {
  115. RedisCacheManager redisCacheManager = new RedisCacheManager();
  116. redisCacheManager.setRedisManager(redisManager());
  117. return redisCacheManager;
  118. }
  119. /**
  120. * RedisSessionDAO shiro sessionDao層的實現 通過redis
  121. * <p>
  122. * 使用的是shiro-redis開源外掛
  123. */
  124. @Bean
  125. public RedisSessionDAO redisSessionDAO() {
  126. RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
  127. redisSessionDAO.setRedisManager(redisManager());
  128. return redisSessionDAO;
  129. }
  130. /**
  131. * 開啟shiro aop註解支援.
  132. * 使用代理方式;所以需要開啟程式碼支援;
  133. *
  134. * @param securityManager
  135. * @return
  136. */
  137. @Bean
  138. public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
  139. AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
  140. authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
  141. return authorizationAttributeSourceAdvisor;
  142. }
  143. /**
  144. * 註冊全域性異常處理
  145. * @return
  146. */
  147. @Bean(name = "exceptionHandler")
  148. public HandlerExceptionResolver handlerExceptionResolver() {
  149. return new MyExceptionHandler();
  150. }
  151. }

在定義的SessionManager的Bean中返回我們的MySessionManager,然後在SecurityManager的Bean中呼叫setSessionManager(SessionManager sessionManager)方法載入我們的自定義SessionManager。

附上
MyShiroRealm的程式碼

  1. package com.xxx.shiro.config;
  2. import com.xxx.shiro.entity.SysPermission;
  3. import com.xxx.shiro.entity.SysRole;
  4. import com.xxx.shiro.entity.UserInfo;
  5. import com.xxx.shiro.service.UserInfoService;
  6. import org.apache.shiro.authc.*;
  7. import org.apache.shiro.authz.AuthorizationInfo;
  8. import org.apache.shiro.authz.SimpleAuthorizationInfo;
  9. import org.apache.shiro.realm.AuthorizingRealm;
  10. import org.apache.shiro.subject.PrincipalCollection;
  11. import org.apache.shiro.util.ByteSource;
  12. import javax.annotation.Resource;
  13. /**
  14. * Created by Administrator on 2017/12/11.
  15. * 自定義許可權匹配和賬號密碼匹配
  16. */
  17. public class MyShiroRealm extends AuthorizingRealm {
  18. @Resource
  19. private UserInfoService userInfoService;
  20. @Override
  21. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  22. // System.out.println("許可權配置-->MyShiroRealm.doGetAuthorizationInfo()");
  23. SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
  24. UserInfo userInfo = (UserInfo) principals.getPrimaryPrincipal();
  25. for (SysRole role : userInfo.getRoleList()) {
  26. authorizationInfo.addRole(role.getRole());
  27. for (SysPermission p : role.getPermissions()) {
  28. authorizationInfo.addStringPermission(p.getPermission());
  29. }
  30. }
  31. return authorizationInfo;
  32. }
  33. /*主要是用來進行身份認證的,也就是說驗證使用者輸入的賬號和密碼是否正確。*/
  34. @Override
  35. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
  36. throws AuthenticationException {
  37. // System.out.println("MyShiroRealm.doGetAuthenticationInfo()");
  38. //獲取使用者的輸入的賬號.
  39. String username = (String) token.getPrincipal();
  40. // System.out.println(token.getCredentials());
  41. //通過username從資料庫中查詢 User物件,如果找到,沒找到.
  42. //實際專案中,這裡可以根據實際情況做快取,如果不做,Shiro自己也是有時間間隔機制,2分鐘內不會重複執行該方法
  43. UserInfo userInfo = userInfoService.findByUsername(username);
  44. // System.out.println("----->>userInfo="+userInfo);
  45. if (userInfo == null) {
  46. return null;
  47. }
  48. if (userInfo.getState() == 1) { //賬戶凍結
  49. throw new LockedAccountException();
  50. }
  51. SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
  52. userInfo, //使用者名稱
  53. userInfo.getPassword(), //密碼
  54. ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//salt=username+salt
  55. getName() //realm name
  56. );
  57. return authenticationInfo;
  58. }
  59. }

傳統專案中,登入成功後應該重定向請求,但在前後端分離專案中,通過ajax登入後應該返回登入狀態標誌以及相關資訊。Web層登入方法程式碼如下

  1. /**
  2. * 登入方法
  3. * @param userInfo
  4. * @return
  5. */
  6. @RequestMapping(value = "/ajaxLogin", method = RequestMethod.POST)
  7. @ResponseBody
  8. public String ajaxLogin(UserInfo userInfo) {
  9. JSONObject jsonObject = new JSONObject();
  10. Subject subject = SecurityUtils.getSubject();
  11. UsernamePasswordToken token = new UsernamePasswordToken(userInfo.getUsername(), userInfo.getPassword());
  12. try {
  13. subject.login(token);
  14. jsonObject.put("token", subject.getSession().getId());
  15. jsonObject.put("msg", "登入成功");
  16. } catch (IncorrectCredentialsException e) {
  17. jsonObject.put("msg", "密碼錯誤");
  18. } catch (LockedAccountException e) {
  19. jsonObject.put("msg", "登入失敗,該使用者已被凍結");
  20. } catch (AuthenticationException e) {
  21. jsonObject.put("msg", "該使用者不存在");
  22. } catch (Exception e) {
  23. e.printStackTrace();
  24. }
  25. return jsonObject.toString();
  26. }

本專案使用SpringMVC框架,可以自行修改使用其他MVC框架。登入成功則返回sessionId作為token給前端儲存,前端請求時將該token放入請求頭,以Authorization為key,以此來鑑權。如果出現賬號或密碼錯誤等異常則返回錯誤資訊。

傳統專案中,登出後應重定向請求,到登入介面或其他指定介面,在前後端分離的專案中,我們應該返回json資訊。在上面提到的ShiroConfig中配置了預設登入路由

在Web層加入方法

  1. /**
  2. * 未登入,shiro應重定向到登入介面,此處返回未登入狀態資訊由前端控制跳轉頁面
  3. * @return
  4. */
  5. @RequestMapping(value = "/unauth")
  6. @ResponseBody
  7. public Object unauth() {
  8. Map<String, Object> map = new HashMap<String, Object>();
  9. map.put("code", "1000000");
  10. map.put("msg", "未登入");
  11. return map;
  12. }

此處簡單提示未登入返回狀態碼,也可自行定義資訊。

在專案中,許可權相關表可能不在業務庫中,因此有必要單獨配置許可權相關表的資料來源。詳細配置可以參見《Spring Boot多資料來源配置與使用》一文。

Shiro資料來源配置程式碼

  1. package com.xxx.shiro.datasource;
  2. import java.util.Map;
  3. import javax.persistence.EntityManager;
  4. import javax.sql.DataSource;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.beans.factory.annotation.Qualifier;
  7. import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
  8. import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
  9. import org.springframework.context.annotation.Bean;
  10. import org.springframework.context.annotation.Configuration;
  11. import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  12. import org.springframework.orm.jpa.JpaTransactionManager;
  13. import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
  14. import org.springframework.transaction.PlatformTransactionManager;
  15. import org.springframework.transaction.annotation.EnableTransactionManagement;
  16. /**
  17. * Created by Administrator on 2017/12/11.
  18. */
  19. @Configuration
  20. @EnableTransactionManagement
  21. @EnableJpaRepositories(
  22. entityManagerFactoryRef="shiroEntityManagerFactory",
  23. transactionManagerRef="shiroTransactionManager",
  24. basePackages= { "com.xxx.shiro.dao" })
  25. public class ShiroDataSourceConfig {
  26. @Autowired
  27. private JpaProperties jpaProperties;
  28. @Autowired
  29. @Qualifier("shiroDataSource")
  30. private DataSource shiroDataSource;
  31. @Bean(name = "shiroEntityManager")
  32. public EntityManager shiroEntityManager(EntityManagerFactoryBuilder builder) {
  33. return shiroEntityManagerFactory(builder).getObject().createEntityManager();
  34. }
  35. @Bean(name = "shiroEntityManagerFactory")
  36. public LocalContainerEntityManagerFactoryBean shiroEntityManagerFactory (EntityManagerFactoryBuilder builder) {
  37. return builder
  38. .dataSource(shiroDataSource)
  39. .properties(getVendorProperties(shiroDataSource))
  40. .packages("com.xxx.shiro.entity")
  41. .persistenceUnit("shiroPersistenceUnit")
  42. .build();
  43. }
  44. private Map<String, String> getVendorProperties(DataSource dataSource) {
  45. return jpaProperties.getHibernateProperties(dataSource);
  46. }
  47. @Bean(name = "shiroTransactionManager")
  48. PlatformTransactionManager shiroTransactionManager(EntityManagerFactoryBuilder builder) {
  49. return new JpaTransactionManager(shiroEntityManagerFactory(builder).getObject());
  50. }
  51. }

IDEA下JpaProperties可能會報錯,可以忽略。

入口模組結構如下圖

DataSourceConfig中配置了多個數據源的Bean,其中shiro資料來源Bean程式碼

  1. /**
  2. * shiro資料來源
  3. * @return
  4. */
  5. @Bean(name = "shiroDataSource")
  6. @Qualifier("shiroDataSource")
  7. @ConfigurationProperties(prefix="spring.datasource.shiro")
  8. public DataSource shiroDataSource() {
  9. return DataSourceBuilder.create().build();
  10. }

ServletInitializer和StartApp為SpringBoot在外部tomcat啟動配置,不贅述。

SpringBoot的相關配置在application.yml中,shiro配置程式碼如下圖

Primary為主庫配置。當在某個專案中引入spring-boot-shiro模組時,只需要在配置檔案中加入shiro資料來源及redis的相關配置,並在DataSourceConfig加入shiro資料來源Bean即可。

Shiro框架會根據使用者登入及許可權狀態丟擲異常,建議使用SpringMVC的全域性異常捕獲來處理異常,避免重複程式碼。該專案中程式碼如下

  1. package com.xxx.shiro.config;
  2. import com.alibaba.fastjson.support.spring.FastJsonJsonView;
  3. import org.apache.shiro.authz.UnauthenticatedException;
  4. import org.apache.shiro.authz.UnauthorizedException;
  5. import org.springframework.web.servlet.HandlerExceptionResolver;
  6. import org.springframework.web.servlet.ModelAndView;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import java.util.HashMap;
  10. import java.util.Map;
  11. /**
  12. * Created by Administrator on 2017/12/11.
  13. * 全域性異常處理
  14. */
  15. public class MyExceptionHandler implements HandlerExceptionResolver {
  16. public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception ex) {
  17. ModelAndView mv = new ModelAndView();
  18. FastJsonJsonView view = new FastJsonJsonView();
  19. Map<String, Object> attributes = new HashMap<String, Object>();
  20. if (ex instanceof UnauthenticatedException) {
  21. attributes.put("code", "1000001");
  22. attributes.put("msg", "token錯誤");
  23. } else if (ex instanceof UnauthorizedException) {
  24. attributes.put("code", "1000002");
  25. attributes.put("msg", "使用者無許可權");
  26. } else {
  27. attributes.put("code", "1000003");
  28. attributes.put("msg", ex.getMessage());
  29. }
  30. view.setAttributesMap(attributes);
  31. mv.setView(view);
  32. return mv;
  33. }
  34. }

該Bean在ShiroConfig中已有註冊程式碼。

至此,shiro框架的整合就結束了。至於shiro框架的使用細節,可以自行查閱相關資料。專案程式碼本人測試可正常工作,未應用到生產環境,僅供學習交流使用。

參考文章

1.《在前後端分離的專案中,後臺使用shiro框架時,怎樣使用它的會話管理系統(session),從而實現許可權控制

2.《Spring Boot多資料來源配置與使用

3.《springboot整合shiro-登入認證和許可權管理

需要原始碼的朋友可以看看我的另一篇博文《SpringBoot+Shiro+MyBatisPlus搭建前後端分離的多模組專案

更優雅的原始碼,更新的springboot版本,熱點問題解答,可以看看最新博文《SpringBoot2.0,Thymeleaf與Shiro整合

Spring Boot整合Shiro 許可權管理 在前後端分離的SpringBoot專案中整合Shiro許可權框架Spring Boot整合Shiro 許可權管理 在前後端分離的SpringBoot專案中整合Shiro許可權框架Spring Boot整合Shiro 許可權管理 在前後端分離的SpringBoot專案中整合Shiro許可權框架Spring Boot整合Shiro 許可權管理 在前後端分離的SpringBoot專案中整合Shiro許可權框架Spring Boot整合Shiro 許可權管理 在前後端分離的SpringBoot專案中整合Shiro許可權框架Spring Boot整合Shiro 許可權管理 在前後端分離的SpringBoot專案中整合Shiro許可權框架Spring Boot整合Shiro 許可權管理 在前後端分離的SpringBoot專案中整合Shiro許可權框架Spring Boot整合Shiro 許可權管理 在前後端分離的SpringBoot專案中整合Shiro許可權框架