shiro的权限控制应用,集成spring项目,密码错误次数过多短时间锁定

以前对shiro都是一知半解,最近系统学了一遍shiro并集成到了在做的项目中。

下面就详细向大家描述一下shiro的用法。

首先是对spring的配置文件,如下:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:context="http://www.springframework.org/schema/context"
 5     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 6         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
 7
 8     <!-- 配置自定调用 Shiro 对象 init 和 destroy 方法的 BeanPostProcessor -->
 9     <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
10     <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
11
12     <!-- 配置使 Shiro 注解起作用的两个 bean, 前提是必须配置  LifecycleBeanPostProcessor bean -->
13     <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
14         <property name="proxyTargetClass" value="true" />
15     </bean>
16     <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
17         <property name="securityManager" ref="securityManager"/>
18     </bean>
19
20     <!-- 配置 Shiro 的 CacheManager -->
21     <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
22         <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
23     </bean>
24
25     <!--
26         配置 Realm. 实际进行认证和授权的对象
27         通过 init-method 初始化 credentialsMatcher 属性
28     -->
29     <bean id="myRealm"
30         class="com.activiti.shiro.realms.MyRealm"
31         init-method="initCredentialsMatcher"></bean>
32
33     <!-- 配置 Shiro 的 SecurityManager 实例 -->
34     <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
35         <property name="cacheManager" ref="cacheManager"/>
36         <property name="realm" ref="myRealm"/>
37     </bean>
38
39
40
41     <bean id="filterChainDefinitionMapBuilder"
42         class="com.activiti.shiro.FilterChainDefinitionMapBuilder"></bean>
43
44     <!-- 配置 Shiro Filter. 该 bean 的 id 必须和 web.xml 文件中配置的 filter 的 filter-name 一致 -->
45     <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
46          <!-- 添加各种验证过滤器 -->
47         <property name="filters">
48             <map>
49                 <entry key="roleOrFilter" value-ref="roleOrFilter"/>
50             </map>
51         </property>
52         <property name="securityManager" ref="securityManager"/>
53         <property name="loginUrl" value="/index.jsp"/>
54         <property name="successUrl" value="/list.jsp"/>
55         <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
56         <!-- 配置 filterChainDefinitons 属性. 具体需要拦截哪些资源, 以及访问这些资源需要有哪些权限 -->
57         <!-- 若把资源极其对应的权限放入到数据表中, 则需要注入其 filterChainDefinitionMap, 传入的是一个 Map 类型.  -->
58         <property name="filterChainDefinitionMap">
59             <bean factory-bean="filterChainDefinitionMapBuilder" factory-method="buildFilterChainDefinitionMap"/>
60         </property>
61     </bean>
62
63     <!-- 自定义的过滤器,用来判断当前用户是否是roleOrFilter["comm,test"]中的某个角色 -->
64     <bean id="roleOrFilter" class="com.activiti.shiro.RolesAuthorizationFilter" />
65
66 </beans>

其中12行到18行代码,如果不使用shiro注解的话,可以去掉。

还有关于cache的配置信息也可以去掉。

接下来我们来梳理配置文件中定义的bean,对于上面那些固定搭配我就不再阐述了,只来看一下需要我们自己配置的。

首先是我们自己定义的myRealm,代码如下:

 1 /**
 2  * 处理shiro登录授权
 3  * @author zhangjiahui
 4  *
 5  */
 6 @Component
 7 public class MyRealm extends AuthorizingRealm{
 8
 9     @Resource
10     private UserService userService;
11     @Resource
12     private RoleService roleService;
13
14     /**
15      * 授权方法
16      */
17     @Override
18     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
19         //获取登录名
20         Object primaryPrincipal = principals.getPrimaryPrincipal();
21         //根据登录名获取角色放入roles中
22         User user = userService.getUserByLoginName(primaryPrincipal.toString());
23         Set<String> roles = user.getRoles();
24         SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
25         return info;
26     }
27
28     /**
29      * 验证登录
30      */
31     @Override
32     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
33
34         System.out.println("======开始验证登录======");
35         //从token中获取到登录信息
36         UsernamePasswordToken usernamePassword = (UsernamePasswordToken) token;
37         //获取用户名
38         Object principal = usernamePassword.getPrincipal();
39         //获取当前realm的名称
40         String realmName = getName();
41         //从数据库中取出的密码
42         User user = userService.getUserByLoginName(principal.toString());
43         if(user==null){
44             throw new UnknownAccountException();
45         }
46         String passWord = user.getPassword();
47         //将得到的密码盐值加密
48         Object hashedCredentials = this.getSaltPassWord(passWord);
49         //定义的盐值
50         String salt = "www.yiwukong.com";
51         ByteSource credentialsSalt = ByteSource.Util.bytes(salt.getBytes());
52         //生成对象,交给shiro去验证
53         SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, hashedCredentials, credentialsSalt, realmName);
54         return info;
55     }
56
57     /**
58      * 初始化方法
59      */
60     public void initCredentialsMatcher(){
61         HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
62         credentialsMatcher.setHashAlgorithmName("MD5");
63         credentialsMatcher.setHashIterations(1024);
64         setCredentialsMatcher(credentialsMatcher);
65     }
66
67     private Object getSaltPassWord(String passWord){
68         String hashAlgorithmName = "MD5";
69         String credentials = passWord;
70         Object salt = ByteSource.Util.bytes("www.yiwukong.com".getBytes());
71         int hashIterations = 1024;
72         Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
73         return result;
74     }
75
76 }

该类需要继承AuthorizingRealm类,然后实现其中的doGetAuthorizationInfo和doGetAuthenticationInfo两个方法,来实现登录及授权。

doGetAuthenticationInfo,登录用的方法登录时候拿到登录名,然后到数据库里面去取出密码,加盐之后交给shiro去比对,当然这里也可以不加盐,具体的操作步骤注释中已经写的很清楚,就不再描述了。

doGetAuthorizationInfo,授权方法,获取到登录名,从数据库中查出他所拥有的角色,封装为Set交给shiro去处理,具体步骤也已在代码中体现。

接下来是我们定义的filterChainDefinitionMapBuilder,代码如下:

 1 package com.activiti.shiro;
 2
 3 import java.util.LinkedHashMap;
 4
 5 import javax.annotation.Resource;
 6
 7 import com.activiti.service.FunctionService;
 8
 9 public class FilterChainDefinitionMapBuilder {
10
11     @Resource
12     private FunctionService functionService;
13
14     /**
15      * 为shiro返回权限信息
16      * @return
17      */
18     public LinkedHashMap<String, Object> buildFilterChainDefinitionMap(){
19
20         LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>();
21
22         result = functionService.getFunctionGroupByRoleList();
23
24 //        result.put("/**", "roles[admin]");
25         result.put("/user/list", "roleOrFilter[people,process]");
26         result.put("/shiro/login", "anon");
27         result.put("/shiro/logout", "logout");
28         System.out.println(result);
29
30         return result;
31     }
32
33 }

在该类中,定义方法,从数据库中查处所有的需要过滤权限的路径以及所对应的拥有该权限的角色名,以map的形式返回,然后用户登录之后需要访问某个路径时候shiro就回去验证是否拥有权限。

之后,在shiroFilter中我们自定义了自己的filter,因为shiro默认的roles这个filter当在其中填入多个角色的时候,默认为用户拥有这些全部的角色才能访问该路径;而我们所要实现的需求肯定是用户拥有其中一个权限就可以访问该路径,配置文件上面已经描述的很清楚,下面就来看filter的具体代码:

 1 package com.activiti.shiro;
 2
 3 import javax.servlet.ServletRequest;
 4 import javax.servlet.ServletResponse;
 5
 6 import org.apache.shiro.subject.Subject;
 7 import org.apache.shiro.web.filter.authz.AuthorizationFilter;
 8
 9 public class RolesAuthorizationFilter extends AuthorizationFilter{
10
11     @Override
12     protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
13         Subject subject = getSubject(request, response);
14         String[] rolesArray = (String[]) mappedValue;
15
16         if (rolesArray == null || rolesArray.length == 0) { //没有角色限制,有权限访问
17             return false;
18         }
19         for (int i = 0; i < rolesArray.length; i++) {
20             if (subject.hasRole(rolesArray[i])) { //若当前用户是rolesArray中的任何一个,则有权限访问
21                 return true;
22             }
23         }
24
25         return false;
26     }
27
28 }

我们所写的filter需要继承授权的AuthorizationFilter,重写isAccessAllowed方法,具体实现注释中已写明。

至此shiro的基本实现,已完成。

下面在说一下,密码输错次数太多,短时间锁定用户的做法。这里就用到了上面配置的ehcache,需要在spring配置文件里面加入如下代码:

 1 <!-- 配置 Shiro 的 CacheManager -->
 2     <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
 3         <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
 4     </bean>
 5
 6     <bean id="credentialsMatcher" class="com.sshhyy.security.CustomCredentialsMatcher">
 7         <constructor-arg ref="cacheManager" />
 8         <property name="hashAlgorithmName" value="md5" />
 9         <property name="hashIterations" value="3" />
10         <property name="storedCredentialsHexEncoded" value="true" />
11     </bean>

其中配置cache的步骤在上面已经提到,而配置credentialsMatcher是为了自定义密码验证方法。

其中echache配置代码如下:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <ehcache name="shirocache">
 3
 4     <diskStore path="java.io.tmpdir" />
 5
 6     <!-- 登录记录缓存 锁定10分钟 -->
 7     <cache name="passwordRetryCache" eternal="false"
 8         timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
 9         statistics="true">
10     </cache>
11
12     <cache name="authorizationCache" eternal="false"
13         timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
14         statistics="true">
15     </cache>
16
17     <cache name="authenticationCache" eternal="false"
18         timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
19         statistics="true">
20     </cache>
21
22     <cache name="shiro-activeSessionCache" eternal="false"
23         timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
24         statistics="true">
25     </cache>
26
27 </ehcache>  

自定义的credentialsMatcher代码如下:

 1 import java.util.concurrent.atomic.AtomicInteger;
 2
 3 import org.apache.shiro.authc.AuthenticationInfo;
 4 import org.apache.shiro.authc.AuthenticationToken;
 5 import org.apache.shiro.authc.ExcessiveAttemptsException;
 6 import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
 7 import org.apache.shiro.cache.Cache;
 8 import org.apache.shiro.cache.CacheManager;
 9
10 /**
11  * @author zhangjiahui
12  */
13 public class CustomCredentialsMatcher extends HashedCredentialsMatcher {
14
15     private Cache<String,AtomicInteger> passwordRetryCache;
16
17     public CustomCredentialsMatcher(CacheManager cacheManager) {
18         passwordRetryCache = cacheManager.getCache("passwordRetryCache");
19     }
20
21     @Override
22     public boolean doCredentialsMatch(AuthenticationToken token,
23             AuthenticationInfo info) {
24         String loginName = (String) token.getPrincipal();
25         AtomicInteger retryCount = passwordRetryCache.get(loginName);
26         if(retryCount==null){
27             retryCount = new AtomicInteger();
28             passwordRetryCache.put(loginName, retryCount);
29         }
30         if(retryCount.incrementAndGet()>5){
31             throw new ExcessiveAttemptsException();
32         }
33         boolean matchs = super.doCredentialsMatch(token, info);
34         if(matchs){
35             passwordRetryCache.remove(loginName);
36         }
37         return super.doCredentialsMatch(token, info);
38     }
39
40     public Cache<String, AtomicInteger> getPasswordRetryCache() {
41         return passwordRetryCache;
42     }
43
44     public void setPasswordRetryCache(Cache<String, AtomicInteger> passwordRetryCache) {
45         this.passwordRetryCache = passwordRetryCache;
46     }
47
48 }

这样当密码输错五次时,就会抛出账号密码输入次数过多异常,十分钟后cache中记录会清空。

controller层的登录代码:

 1 public Map<String, String> login(HttpServletRequest request, Model model) {
 2         Map<String, String> map = new HashMap<String, String>();
 3         map.put("returnCode", ERR_CODE);
 4         String msg = "";
 5         String nameLogin = request.getParameter(SessionEnum.CURRENT_LOGIN_NAME);
 6         if (StringUtil.isBlank(nameLogin)) {
 7             msg = "请输入登录名!";
 8             model.addAttribute("message", msg);
 9         }
10         String password = request.getParameter(SessionEnum.CURRENT_LOGIN_PASSWORD);
11         if (StringUtil.isBlank(password)) {
12             msg = "密码不能为空!";
13             model.addAttribute("message", msg);
14         }
15         boolean flag = false;
16         String isRememberMe = request.getParameter(SessionEnum.IS_REMEMBER_ME);
17         if (null == isRememberMe)
18             flag = true;
19
20         UsernamePasswordToken token = new UsernamePasswordToken(nameLogin, password);
21         token.setRememberMe(flag);
22         try {
23             Subject subject = SecurityUtils.getSubject();
24             subject.login(token);
25         } catch (IncorrectCredentialsException e) {
26             e.printStackTrace();
27             msg = "登录名或密码错误,请重新登录!";
28             model.addAttribute("message", msg);
29         } catch (ExcessiveAttemptsException e) {
30             e.printStackTrace();
31             msg = "登录失败次数过多";
32             model.addAttribute("message", msg);
33         } catch (LockedAccountException e) {
34             e.printStackTrace();
35             msg = "帐号("+token.getPrincipal()+")已被锁定!";
36             model.addAttribute("message", msg);
37         } catch (DisabledAccountException e) {
38             e.printStackTrace();
39             msg = "帐号("+token.getPrincipal()+")已被禁用!";
40             model.addAttribute("message", msg);
41         } catch (ExpiredCredentialsException e) {
42             e.printStackTrace();
43             msg = "帐号("+token.getPrincipal()+")已过期!";
44             model.addAttribute("message", msg);
45         } catch (UnknownAccountException e) {
46             e.printStackTrace();
47             msg = "登录名或密码错误,请重新登录!";
48             model.addAttribute("message", msg);
49         } catch (UnauthorizedException e) {
50             e.printStackTrace();
51             msg = "您没有得到相应的授权!" + e.getMessage();
52             model.addAttribute("message", msg);
53         } catch (NullPointerException e) {
54             e.printStackTrace();
55             msg = "输入框不能为空!";
56             model.addAttribute("message", msg);
57         }
58         map.put("message", msg);
59         return map;
60     }

至此shiro配置成功,对于一般的项目来说应该是够用了,至少目前来说对于我够用了,哈哈。

时间: 2024-10-22 15:05:30

shiro的权限控制应用,集成spring项目,密码错误次数过多短时间锁定的相关文章

shiro进行权限控制的四种方式

```我们使用shiro进行权限控制 有以下几种方式 1. URL拦截权限控制:基于filter过滤器实现 我们在spring配置文件中配置shiroFilter时配置 <!--指定URL级别拦截策略 --> <property name="filterChainDefinitions"> <value> /css/ = anon /js/ = anon /images/ = anon /validatecode.jsp = anon /login.

Spring Security应用开发(09)密码错误次数限制

实现登录时密码错误次数限制功能,就是在登录界面中当用户提交了错误的密码时在数据库中记录下这个错误次数,直到错误次数达到指定次数时,锁定用户账户,此时即便输入正确的密码,也不能登录. 需要完成如下工作: (1)修改用户表users的结构,增加相关字段. (2)自定义实现UserDetailsService,用于加载额外的数据字段. (3)自定义实现AuthenticationProvider,用于捕获登录成功和失败的事件. (3)修改spring-security.xml文件,配置上述(2)和(3

web工程使用spring mvc+shiro进行权限控制

第1步:引入shiro相关jar包 ehcache-core-2.5.0.jar shiro-ehcache-1.2.3.jar shiro-core-1.2.3.jar shiro-web-1.2.3.jar shiro-spring-1.2.3.jar 第二步:web.xml配置 <!-- shiro的filter --> <!-- shiro过虑器,DelegatingFilterProxy通过代理模式将spring容器中的bean和filter关联起来 --> <fi

spring项目启动错误——java.lang.NoClassDefFoundError: org/springframework/context/ApplicationContext

最近在搭spring项目框架的时候,遇到一个很伤的问题,翻了很多帖,都报告说什么少spring-context包啊之类的,但实际上spring的那些依赖我根本没漏,下面是我的pom: <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version

Web应用程序系统的多用户权限控制设计及实现-项目架构【3】

本章主要讲述Web权限管理系统的项目架构,及开发中需要的基本类和相关的CSS,JS文件. 1.1系统结构 本系统搭建开发工具为Visual Studio 2012,采用ASP.NET MVC 4.0技术开发.系统的框架图如下所示: 特别说明:系统需要用到的CSS文件在Content目录下,公有的JS文件在Scripts目录下.其下载链接为:http://files.cnblogs.com/files/wlandwl/CSS_JS.zip 系统页面前台展示主要运用EasyUI1.4.3的展示控件及

shiro注解权限控制-5个权限注解

RequiresAuthentication: 使用该注解标注的类,实例,方法在访问或调用时,当前Subject必须在当前session中已经过认证 RequiresGuest: 使用该注解标注的类,实例,方法在访问或调用时,当前Subject可以是"gust"身份,不需要经过认证或者在原先的session中存在记录. RequiresPermissions: 当前Subject需要拥有某些特定的权限时,才能执行被该注解标注的方法.如果当前Subject不具有这样的权限,则方法不会被执

Springmvc集成Shiro实现权限管理

Shiro是一个安全框架,他可以集成其他开发开发框架 如:Springmvc,实现用户身份认证.权限管理等等功能,shiro详细的介绍也就不讲了,这里给出一些关键的知识点吧: 知识点: shiro中默认的过滤器 过滤器名称 过滤器类 描述 anon org.apache.shiro.web.filter.authc.AnonymousFilter 匿名过滤器 authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter 如果继续

了解权限控制框架shiro 之实际应用.

Apache Shiro 1.权限控制分为 a.粗粒度 URL 级别权限控制     b.细粒度方法级别权限控制 2.使用shiro进行权限控制主要有四种主要方式 : a. 在程序中 通过 Subject 编程方式进行权限控制 b. 配置 Filter 实现 URL 级别粗粒度权限控制 c. 配置代理,基于注解实现细粒度权限控制 d. 在页面中使用 shiro 自定义标签实现 页面显示权限控制 3.shiro实际应用之基本配置: a.用父工程引入shiro b.配置web.xml c.配置app

关于细粒度权限控制的考量

权限控制可以说是每个项目的必备基础模块,不讨论RBAC和ACL,只是自己的想法. 我眼中的权限控制: 作用:用于控制功能或资源的访问,仅此而已. 无论是SpringMVC的拦截器,还是Struts的拦截器,拦截地址栏操作都是那么的简单. 问题在于如何将权限控制在页面级别,例如,按钮,某个资源元素等. 关于命名约定的问题: 例如添加功能: @RequestMapping(value="/role/add.jhtml",RequestMethod=GET) @RequestMapping(