Shiro采用shiro實現登錄認證與權限授權管理


Shiro 是一個 Apache 下的一開源項目項目,旨在簡化身份驗證和授權。

 

spring,springMVC,maven,shiro

 

shiro的配置,通過maven加入shiro相關jar包

 

1.shiro的配置,通過maven加入shiro相關jar包

 

 1     <!-- shiro -->  
 2         <dependency>  
 3             <groupId>org.apache.shiro</groupId>  
 4             <artifactId>shiro-core</artifactId>  
 5             <version>1.2.1</version>  
 6         </dependency>  
 7         <dependency>  
 8             <groupId>org.apache.shiro</groupId>  
 9             <artifactId>shiro-web</artifactId>  
10             <version>1.2.1</version>  
11         </dependency>  
12         <dependency>  
13             <groupId>org.apache.shiro</groupId>  
14             <artifactId>shiro-ehcache</artifactId>  
15             <version>1.2.1</version>  
16         </dependency>  
17         <dependency>  
18             <groupId>org.apache.shiro</groupId>  
19             <artifactId>shiro-spring</artifactId>  
20             <version>1.2.1</version>  
21         </dependency>  

 

 

2.2 在web.xml中添加shiro過濾器

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3     xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
 4     xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 5     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
 6     id="WebApp_ID" version="2.4">
 7 
 8     <!-- 配置spring容器監聽器 -->
 9 <span style="color:#ff6666;">    <context-param>
10         <param-name>contextConfigLocation</param-name>
11         <param-value>classpath:application-context-*.xml</param-value>
12     </context-param>
13     <listener>
14         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
15     </listener></span>
16     <!-- post亂碼處理 -->
17     <filter>
18         <filter-name>CharacterEncodingFilter</filter-name>
19         <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
20         <init-param>
21             <param-name>encoding</param-name>
22             <param-value>utf-8</param-value>
23         </init-param>
24     </filter>
25     <filter-mapping>
26         <filter-name>CharacterEncodingFilter</filter-name>
27         <url-pattern>/*</url-pattern>
28     </filter-mapping>
29     <!-- 配置shiro的核心攔截器 -->
30 <span style="white-space:pre">    </span><filter>
31         <filter-name>shiroFilter</filter-name>
32         <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
33     </filter>
34     <filter-mapping>
35         <filter-name>shiroFilter</filter-name>
36         <url-pattern>/admin/*</url-pattern>
37     </filter-mapping>
38     <!-- 配置后台Springmvc 它攔截.do結尾的請求 -->
39     <servlet>
40         <servlet-name>back</servlet-name>
41         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
42         <init-param>
43             <param-name>contextConfigLocation</param-name>
44             <param-value>classpath:springmvc.xml</param-value>
45         </init-param>
46     </servlet>
47     <servlet-mapping>
48         <servlet-name>back</servlet-name>
49         <url-pattern>*.do</url-pattern>
50     </servlet-mapping>
51     <display-name>Archetype Created Web Application</display-name>
52 </web-app>

 

3 spring中對shiro配置

  1 <beans xmlns="http://www.springframework.org/schema/beans"
  2     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
  3     xmlns:context="http://www.springframework.org/schema/context"
  4     xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
  5     xsi:schemaLocation="http://www.springframework.org/schema/beans 
  6         http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
  7         http://www.springframework.org/schema/mvc 
  8         http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
  9         http://www.springframework.org/schema/context 
 10         http://www.springframework.org/schema/context/spring-context-3.2.xsd 
 11         http://www.springframework.org/schema/aop 
 12         http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
 13         http://www.springframework.org/schema/tx 
 14         http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
 15 
 16     <!-- web.xml中shiro的filter對應的bean -->
 17     <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
 18         <!-- 管理器,必須設置 -->
 19         <property name="securityManager" ref="securityManager" />
 20         <!-- 攔截到,跳轉到的地址,通過此地址去認證 -->
 21         <property name="loginUrl" value="/admin/login.do" />
 22         <!-- 認證成功統一跳轉到/admin/index.do,建議不配置,shiro認證成功自動到上一個請求路徑 -->
 23         <property name="successUrl" value="/admin/index.do" />
 24         <!-- 通過unauthorizedUrl指定沒有權限操作時跳轉頁面 -->
 25         <property name="unauthorizedUrl" value="/refuse.jsp" />
 26         <!-- 自定義filter,可用來更改默認的表單名稱配置 -->
 27         <property name="filters">
 28             <map>
 29                 <!-- 將自定義 的FormAuthenticationFilter注入shiroFilter中 -->
 30                 <entry key="authc" value-ref="formAuthenticationFilter" />
 31             </map>
 32         </property>
 33         <property name="filterChainDefinitions">
 34             <value>
 35                 <!-- 對靜態資源設置匿名訪問 -->
 36                 /images/** = anon
 37                 /js/** = anon
 38                 /styles/** = anon
 39                 <!-- 驗證碼,可匿名訪問 -->
 40                 /validatecode.jsp = anon
 41                 <!-- 請求 logout.do地址,shiro去清除session -->
 42                 /admin/logout.do = logout
 43                 <!--商品查詢需要商品查詢權限 ,取消url攔截配置,使用注解授權方式 -->
 44                 <!-- /items/queryItems.action = perms[item:query] /items/editItems.action 
 45                     = perms[item:edit] -->
 46                 <!-- 配置記住我或認證通過可以訪問的地址 -->
 47                 /welcome.jsp = user
 48                 /admin/index.do = user
 49                 <!-- /** = authc 所有url都必須認證通過才可以訪問 -->
 50                 /** = authc
 51             </value>
 52         </property>
 53     </bean>
 54 
 55     <!-- securityManager安全管理器 -->
 56     <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
 57         <property name="realm" ref="customRealm" />
 58         <!-- 注入緩存管理器 -->
 59         <property name="cacheManager" ref="cacheManager" />
 60         <!-- 注入session管理器 -->
 61         <!-- <property name="sessionManager" ref="sessionManager" /> -->
 62         <!-- 記住我 -->
 63         <property name="rememberMeManager" ref="rememberMeManager" />
 64     </bean>
 65 
 66     <!-- 自定義realm -->
 67     <bean id="customRealm" class="com.zhijianj.stucheck.shiro.CustomRealm">
 68         <!-- 將憑證匹配器設置到realm中,realm按照憑證匹配器的要求進行散列 -->
 69         <!-- <property name="credentialsMatcher" ref="credentialsMatcher" /> -->
 70     </bean>
 71 
 72     <!-- 憑證匹配器 -->
 73     <bean id="credentialsMatcher"
 74         class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
 75         <!-- 選用MD5散列算法 -->
 76         <property name="hashAlgorithmName" value="md5" />
 77         <!-- 進行一次加密 -->
 78         <property name="hashIterations" value="1" />
 79     </bean>
 80 
 81     <!-- 自定義form認證過慮器 -->
 82     <!-- 基於Form表單的身份驗證過濾器,不配置將也會注冊此過慮器,表單中的用戶賬號、密碼及loginurl將采用默認值,建議配置 -->
 83     <!-- 可通過此配置,判斷驗證碼 -->
 84     <bean id="formAuthenticationFilter"
 85         class="com.zhijianj.stucheck.shiro.CustomFormAuthenticationFilter ">
 86         <!-- 表單中賬號的input名稱,默認為username -->
 87         <property name="usernameParam" value="username" />
 88         <!-- 表單中密碼的input名稱,默認為password -->
 89         <property name="passwordParam" value="password" />
 90         <!-- 記住我input的名稱,默認為rememberMe -->
 91         <property name="rememberMeParam" value="rememberMe" />
 92     </bean>
 93     <!-- 會話管理器 -->
 94     <bean id="sessionManager"
 95         class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
 96         <!-- session的失效時長,單位毫秒 -->
 97         <property name="globalSessionTimeout" value="600000" />
 98         <!-- 刪除失效的session -->
 99         <property name="deleteInvalidSessions" value="true" />
100     </bean>
101     <!-- 緩存管理器 -->
102     <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
103         <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml" />
104     </bean>
105     <!-- rememberMeManager管理器,寫cookie,取出cookie生成用戶信息 -->
106     <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
107         <property name="cookie" ref="rememberMeCookie" />
108     </bean>
109     <!-- 記住我cookie -->
110     <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
111         <!-- rememberMe是cookie的名字 -->
112         <constructor-arg value="rememberMe" />
113         <!-- 記住我cookie生效時間30天 -->
114         <property name="maxAge" value="2592000" />
115     </bean>
116 </beans>

 

在上面的配置中每次開啟了sessionManager都會出問題在這里將它注釋掉了。

其次,上面中的配置也是將credentialsMatcher沒有加入了,這種方式適用於沒有對密碼進行處理的情況。

其中CustomRealm的配置是重點。

 

4 自定義Realm編碼

CustomRealm如下:

 1 public class CustomRealm extends AuthorizingRealm {
 2     // 設置realm的名稱
 3     @Override
 4     public void setName(String name) {
 5         super.setName("customRealm");
 6     }
 7 
 8     @Autowired
 9     private AdminUserService adminUserService;
10 
11     /**
12      * 認證
13      */
14     @Override
15     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
16 
17         // token中包含用戶輸入的用戶名和密碼
18         // 第一步從token中取出用戶名
19         String userName = (String) token.getPrincipal();
20         // 第二步:根據用戶輸入的userCode從數據庫查詢
21         TAdminUser adminUser = adminUserService.getAdminUserByUserName(userName);
22         // 如果查詢不到返回null
23         if (adminUser == null) {//
24             return null;
25         }
26         // 獲取數據庫中的密碼
27         String password = adminUser.getPassword();
28         /**
29          * 認證的用戶,正確的密碼
30          */
31         AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(adminUser, password, this.getName());
32                //MD5 加密+加鹽+多次加密
33 //<span style="color:#ff0000;">SimpleAuthenticationInfo authcInfo = new SimpleAuthenticationInfo(adminUser, password,ByteSource.Util.bytes(salt), this.getName());</span>
34         return authcInfo;
35     }
36 
37     /**
38      * 授權,只有成功通過<span style="font-family: Arial, Helvetica, sans-serif;">doGetAuthenticationInfo方法的認證后才會執行。</span>
39      */
40     @Override
41     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
42         // 從 principals獲取主身份信息
43         // 將getPrimaryPrincipal方法返回值轉為真實身份類型(在上邊的doGetAuthenticationInfo認證通過填充到SimpleAuthenticationInfo中身份類型),
44         TAdminUser activeUser = (TAdminUser) principals.getPrimaryPrincipal();
45         // 根據身份信息獲取權限信息
46         // 從數據庫獲取到權限數據
47         TAdminRole adminRoles = adminUserService.getAdminRoles(activeUser);
48         // 單獨定一個集合對象
49         List<String> permissions = new ArrayList<String>();
50         if (adminRoles != null) {
51             permissions.add(adminRoles.getRoleKey());
52         }
53         // 查到權限數據,返回授權信息(要包括 上邊的permissions)
54         SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
55         // 將上邊查詢到授權信息填充到simpleAuthorizationInfo對象中
56         simpleAuthorizationInfo.addStringPermissions(permissions);
57         return simpleAuthorizationInfo;
58     }
59 
60     // 清除緩存
61     public void clearCached() {
62         PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
63         super.clearCache(principals);
64     }
65 }

 

5 緩存配置

ehcache.xml代碼如下:

 1 <ehcache updateCheck="false" name="shiroCache">
 2     <defaultCache
 3             maxElementsInMemory="10000"
 4             eternal="false"
 5             timeToIdleSeconds="120"
 6             timeToLiveSeconds="120"
 7             overflowToDisk="false"
 8             diskPersistent="false"
 9             diskExpiryThreadIntervalSeconds="120"
10             />
11 </ehcache>

 

通過使用ehache中就避免第次都向服務器發送權限授權(doGetAuthorizationInfo)的請求。

6.自定義表單編碼過濾器

CustomFormAuthenticationFilter代碼,認證之前調用,可用於驗證碼校驗

 

 1 public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {
 2     // 原FormAuthenticationFilter的認證方法
 3     @Override
 4     protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
 5         // 在這里進行驗證碼的校驗
 6 
 7         // 從session獲取正確驗證碼
 8         HttpServletRequest httpServletRequest = (HttpServletRequest) request;
 9         HttpSession session = httpServletRequest.getSession();
10         // 取出session的驗證碼(正確的驗證碼)
11         String validateCode = (String) session.getAttribute("validateCode");
12 
13         // 取出頁面的驗證碼
14         // 輸入的驗證和session中的驗證進行對比
15         String randomcode = httpServletRequest.getParameter("randomcode");
16         if (randomcode != null && validateCode != null && !randomcode.equals(validateCode)) {
17             // 如果校驗失敗,將驗證碼錯誤失敗信息,通過shiroLoginFailure設置到request中
18             httpServletRequest.setAttribute("shiroLoginFailure", "randomCodeError");
19             // 拒絕訪問,不再校驗賬號和密碼
20             return true;
21         }
22         return super.onAccessDenied(request, response);
23     }
24 }

 

在此符上驗證碼jsp界面的代碼

validatecode.jsp

 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8"%>
 3 <%@ page import="java.util.Random"%>
 4 <%@ page import="java.io.OutputStream"%>
 5 <%@ page import="java.awt.Color"%>
 6 <%@ page import="java.awt.Font"%>
 7 <%@ page import="java.awt.Graphics"%>
 8 <%@ page import="java.awt.image.BufferedImage"%>
 9 <%@ page import="javax.imageio.ImageIO"%>
10 <%
11     int width = 60;
12     int height = 32;
13     //create the image
14     BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
15     Graphics g = image.getGraphics();
16     // set the background color
17     g.setColor(new Color(0xDCDCDC));
18     g.fillRect(0, 0, width, height);
19     // draw the border
20     g.setColor(Color.black);
21     g.drawRect(0, 0, width - 1, height - 1);
22     // create a random instance to generate the codes
23     Random rdm = new Random();
24     String hash1 = Integer.toHexString(rdm.nextInt());
25     // make some confusion
26     for (int i = 0; i < 50; i++) {
27         int x = rdm.nextInt(width);
28         int y = rdm.nextInt(height);
29         g.drawOval(x, y, 0, 0);
30     }
31     // generate a random code
32     String capstr = hash1.substring(0, 4);
33     //將生成的驗證碼存入session
34     session.setAttribute("validateCode", capstr);
35     g.setColor(new Color(0, 100, 0));
36     g.setFont(new Font("Candara", Font.BOLD, 24));
37     g.drawString(capstr, 8, 24);
38     g.dispose();
39     //輸出圖片
40     response.setContentType("image/jpeg");
41     out.clear();
42     out = pageContext.pushBody();
43     OutputStream strm = response.getOutputStream();
44     ImageIO.write(image, "jpeg", strm);
45     strm.close();
46 %>

 

7.登錄控制器方法

 1     /**
 2      * 到登錄界面
 3      * 
 4      * @return
 5      * @throws Exception
 6      */
 7     @RequestMapping("login.do")
 8     public String adminPage(HttpServletRequest request) throws Exception {
 9         // 如果登陸失敗從request中獲取認證異常信息,shiroLoginFailure就是shiro異常類的全限定名
10         String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
11         // 根據shiro返回的異常類路徑判斷,拋出指定異常信息
12         if (exceptionClassName != null) {
13             if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
14                 // 最終會拋給異常處理器
15                 throw new CustomJsonException("賬號不存在");
16             } else if (IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
17                 throw new CustomJsonException("用戶名/密碼錯誤");
18             } else if ("randomCodeError".equals(exceptionClassName)) {
19                 throw new CustomJsonException("驗證碼錯誤 ");
20             } else {
21                 throw new Exception();// 最終在異常處理器生成未知錯誤
22             }
23         }
24         // 此方法不處理登陸成功(認證成功),shiro認證成功會自動跳轉到上一個請求路徑
25         // 登陸失敗還到login頁面
26         return "admin/login";
27     }

 

8.用戶回顯Controller

當用戶登錄認證成功后,CustomRealm在調用完doGetAuthenticationInfo時,通過

 1 AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(adminUser, password, this.getName()); 2 return authcInfo; 

 

SimpleAuthenticationInfo構造參數的第一個參數傳入一個用戶的對象,之后,可通過Subject subject = SecurityUtils.getSubject();中的subject.getPrincipal()獲取到此對象。

所以需要回顯用戶信息時,我這樣調用的

 1     @RequestMapping("index.do")
 2     public String index(Model model) {
 3         //從shiro的session中取activeUser
 4         Subject subject = SecurityUtils.getSubject();
 5         //取身份信息
 6         TAdminUser adminUser =     (TAdminUser) subject.getPrincipal();
 7         //通過model傳到頁面
 8         model.addAttribute("adminUser", adminUser);
 9         return "admin/index";
10     }

9.在jsp頁面中控制權限

先引入shiro的頭文件

 1 <!-- shiro頭引入 --> 2 <%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro"%> 

采用shiro標簽對權限進行處理

1 <!-- 有curd權限才顯示修改鏈接,沒有該 權限不顯示,相當 於if(hasPermission(curd)) -->
2                 <shiro:hasPermission name="curd">
3                     <BR />
4                     我擁有超級的增刪改查權限額
5                 </shiro:hasPermission>

 

10.在Controller控制權限

通過@RequiresPermissions注解,指定執行此controller中某個請求方法需要的權限
1 @RequestMapping("/queryInfo.do")
2     @RequiresPermissions("q")//執行需要"q"權限
3     public ModelAndView queryItems(HttpServletRequest request) throws Exception {

11.MD5加密加鹽處理

這里以修改密碼為例,通過獲取新的密碼(明文)后通過MD5加密+加鹽+6次加密為例
 1 @RequestMapping("updatePassword.do")
 2     @ResponseBody
 3     public String updateAdminUserPassword(String newPassword) {
 4         // 從shiro的session中取activeUser
 5         Subject subject = SecurityUtils.getSubject();
 6         // 取身份信息
 7         TAdminUser adminUser = (TAdminUser) subject.getPrincipal();
 8         // 生成salt,隨機生成
 9         SecureRandomNumberGenerator secureRandomNumberGenerator = new SecureRandomNumberGenerator();
10         String salt = secureRandomNumberGenerator.nextBytes().toHex();
11         Md5Hash md5 = new Md5Hash(newPassword, salt, 6);
12         String newMd5Password = md5.toHex();
13         // 設置新密碼
14         adminUser.setPassword(newMd5Password);
15         // 設置鹽
16         adminUser.setSalt(salt);
17         adminUserService.updateAdminUserPassword(adminUser);
18         return newPassword;
19     }

ok!編碼完成后對此WEB程序跑一下,截圖符幾張:

當前用戶信息和權限信息表截圖如下

系統管理員表:


權限表:


管理員權限關系中間表:




 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM