Shiro采用shiro实现登录认证与权限授权管理

时间:2023-01-31 15:23:04

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程序跑一下,截图符几张:

当前用户信息和权限信息表截图如下

系统管理员表:

Shiro采用shiro实现登录认证与权限授权管理

权限表:

Shiro采用shiro实现登录认证与权限授权管理

管理员权限关系中间表:

Shiro采用shiro实现登录认证与权限授权管理

Shiro采用shiro实现登录认证与权限授权管理


Shiro采用shiro实现登录认证与权限授权管理