Shiro自定义Realm时用注解的方式注入父类的credentialsMatcher
用Shiro做登录权限控制时,密码加密是自定义的。
数据库的密码通过散列获取,如下,算法为:md5,盐为一个随机数字,散列迭代次数为3次,最终将salt与散列后的密码保存到数据库内,第二次登录时将登录的令牌再进行同样的运算后再与数据库的做对比。
- String algorithmName = "md5";
String userName = "rose";
String password = "rose123";
int hashIterations = 3; //散列迭代次数
String salt = new SecureRandomNumberGenerator().nextBytes().toHex();
// 将用户的密码经过散列算法替换成一个不可逆的新密码保存进数据,散列过程使用了盐
SimpleHash simpleHash = new SimpleHash(algorithmName,password,userName+salt,hashIterations);
String encodedPassword = simpleHash.toHex();
System.out.println("salt is "+salt);
System.out.println("encodedPassword is "+encodedPassword);
创建RetryLimitHashedCredentialsMatcher类,此类有登录失败次数的判断,多于5次后再等待10分钟后才能重试。
缓存机制用到了Ehcache,Ehcache是很多Java项目中使用的缓存框架,Hibernate就是其中之一。它的本质就是将原本只能存储在内存中的数据通过算法保存到硬盘上,再根据需求依次取出。你可以把Ehcache理解为一个Map<String,Object>对象,通过put保存对象,再通过get取回对象。
- package com.ken.shiro;
- import org.apache.shiro.authc.AuthenticationInfo;
- import org.apache.shiro.authc.AuthenticationToken;
- import org.apache.shiro.authc.ExcessiveAttemptsException;
- import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
- import org.apache.shiro.cache.Cache;
- import org.apache.shiro.cache.CacheManager;
- import java.util.concurrent.atomic.AtomicInteger;
- public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
- private Cache<String,AtomicInteger> passwordRetryCache;
- public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager){
- passwordRetryCache = cacheManager.getCache("passwordRetryCache");
- }
- @Override
- public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
- String userName = token.getPrincipal().toString();
- System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
- System.out.println(userName);
- System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
- AtomicInteger retryCount = passwordRetryCache.get(userName);
- if (null == retryCount) {
- retryCount = new AtomicInteger(0);
- passwordRetryCache.put(userName,retryCount);
- }
- if (retryCount.incrementAndGet() > 5) {
- throw new ExcessiveAttemptsException();
- }
- boolean matches = super.doCredentialsMatch(token, info);
- if (matches){
- passwordRetryCache.remove(userName);
- }
- return matches;
- }
- }
spring-shiro.xml内加入配置
- <!-- 数据库保存的密码是使用MD5算法加密的,所以这里需要配置一个密码匹配对象 -->
- <!--<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.Md5CredentialsMatcher"></bean>-->
- <bean id="credentialsMatcher" class="com.ken.shiro.RetryLimitHashedCredentialsMatcher">
- <constructor-arg ref="cacheManager"></constructor-arg>
- <property name="hashAlgorithmName" value="md5"></property><!--加密算法为md5-->
- <property name="hashIterations" value="3"></property><!--3次md5迭代-->
- <!--是否存储散列后的密码为16进制,需要和生成密码时的一样,默认是base64-->
- <property name="storedCredentialsHexEncoded" value="true"></property>
- </bean>
- <!--缓存管理-->
<!--<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.MemoryConstrainedCacheManager"></bean>-->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager" >
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property>
</bean>
ehcache.xml的配置如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <ehcache name="shirocache">
- <diskStore path="java.io.tmpdir" />
- <!--
- name:缓存名称。
- maxElementsInMemory:缓存最大个数。
- eternal:对象是否永久有效,一但设置了,timeout将不起作用。
- timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
- timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
- overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
- diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
- maxElementsOnDisk:硬盘最大缓存个数。
- diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
- diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
- memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
- clearOnFlush:内存数量最大时是否清除。
- -->
- <defaultCache
- maxElementsInMemory="10000"
- eternal="false"
- timeToIdleSeconds="120"
- timeToLiveSeconds="120"
- overflowToDisk="true"
- maxElementsOnDisk="10000000"
- diskPersistent="false"
- diskExpiryThreadIntervalSeconds="120"
- memoryStoreEvictionPolicy="LRU"
- />
- <!-- 登录记录缓存 锁定10分钟 -->
- <cache name="passwordRetryCache"
- eternal="false"
- maxElementsInMemory="0"
- timeToIdleSeconds="3600"
- timeToLiveSeconds="0"
- overflowToDisk="false"
- statistics="true">
- </cache>
- <cache name="authorizationCache" eternal="false" maxElementsInMemory="0"
- timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
- statistics="true">
- </cache>
- <cache name="authenticationCache" eternal="false" maxElementsInMemory="0"
- timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
- statistics="true">
- </cache>
- <cache name="shiro-activeSessionCache" eternal="false" maxElementsInMemory="0"
- timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
- statistics="true">
- </cache>
- </ehcache>
实现Realm类MyShiro继承自AuthorizingRealm,AuthorizingRealm实现它的抽象方法doGetAuthorizationInfo权限角色进行配置,AuthorizingRealm又继承自AuthenticatingRealm,AuthenticatingRealm也有一个抽象方法doGetAuthenticationInfo,实现doGetAuthenticationInfo方法对登录的令牌等信息进行验证。
myShiro的职则是对登录进行授权,对角色、权限进行验证等。
- package com.ken.service.impl;
- import com.ken.entity.TRole;
- import com.ken.entity.TUser;
- import com.ken.service.IUserService;
- import org.apache.shiro.SecurityUtils;
- import org.apache.shiro.authc.*;
- import org.apache.shiro.authc.credential.CredentialsMatcher;
- import org.apache.shiro.authz.AuthorizationInfo;
- import org.apache.shiro.authz.SimpleAuthorizationInfo;
- import org.apache.shiro.realm.AuthorizingRealm;
- import org.apache.shiro.session.Session;
- import org.apache.shiro.subject.PrincipalCollection;
- import org.apache.shiro.subject.Subject;
- import org.apache.shiro.util.ByteSource;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import java.util.List;
- @Service
- public class MyShiro extends AuthorizingRealm {
- @Autowired
- IUserService userService;
- @Autowired //注入父类的属性,注入加密算法匹配密码时使用
- public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher){
- super.setCredentialsMatcher(credentialsMatcher);
- }
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
- System.out.println("######################");
- System.out.println("realm==="+this.getName());
- System.out.println("######################");
- //获取登录时输入的用户名
- String loginName = (String)principalCollection.fromRealm(getName()).iterator().next();
- //获取当前的用户名,跟上面的一样
- String currentUsername = (String)super.getAvailablePrincipal(principalCollection);
- System.out.println(currentUsername);
- TUser user = userService.findUserByName(loginName);
- if (user != null) {
- //权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission)
- SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
- //用户的角色集合
- simpleAuthorizationInfo.setRoles(user.getRolesName());
- //对应角色的权限
- List<TRole> roles = user.getRoles();
- for (TRole role:roles){
- simpleAuthorizationInfo.addStringPermissions(role.getPermissionName());
- }
- return simpleAuthorizationInfo;
- }
- return null;
- }
- //如果验证成功,将返回AuthenticationInfo验证信息;此信息中包含了身份及凭证;如果验证失败将抛出相应的AuthenticationException实现。
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
- throws AuthenticationException {
- UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
- System.out.println("AuthenticationInfo####################");
- System.out.println(((UsernamePasswordToken) authenticationToken).getUsername());
- String pwd = new String(((UsernamePasswordToken) authenticationToken).getPassword());
- System.out.println("getPassword="+pwd);
- System.out.println(authenticationToken.getPrincipal().toString());
- String password = new String((char[])authenticationToken.getCredentials()); //得到密码
- System.out.println(password);
- this.setSession("currentUser",authenticationToken.getPrincipal().toString());
- TUser user = userService.findUserByName(token.getUsername());
- if (user != null) {
- System.out.println("user salt is "+user.getCredentialsSalt());
- //这里获取到数据库的用户名密码,然后验证用户名密码,如果不对则执出异常
- return new SimpleAuthenticationInfo(user.getUserName(),user.getPassword(),
- ByteSource.Util.bytes(user.getCredentialsSalt()) //获取盐
- ,getName());
- }
- return null;
- }
- /**
- * 将一些数据放到ShiroSession中,以便于其它地方使用
- * 比如Controller,使用时直接用HttpSession.getAttribute(key)就可以取到
- */
- private void setSession(Object key,Object value){
- Subject subject = SecurityUtils.getSubject();
- if (null != subject) {
- Session session = subject.getSession();
- if (null != session) {
- session.setAttribute(key,value);
- }
- }
- }
- }
完整的spring-shiro.xml文件如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
- <!-- 配置权限管理器 -->
- <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
- <!-- ref对应我们写的realm MyShiro -->
- <property name="realm" ref="myShiro"></property>
- <property name="cacheManager" ref="cacheManager"></property>
- </bean>
- <!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
- <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
- <!-- 调用我们配置的权限管理器 -->
- <property name="securityManager" ref="securityManager"></property>
- <property name="loginUrl" value="/login"></property>
- <!-- 配置我们在登录页登录成功后的跳转地址,如果你访问的是非/login地址,则跳到您访问的地址 -->
- <property name="successUrl" value="/user"></property>
- <!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->
- <property name="unauthorizedUrl" value="/403"></property>
- <!-- 权限配置 -->
- <property name="filterChainDefinitions">
- <value>
- <!-- anon表示此地址不需要任何权限即可访问 refer to:http://blog.csdn.net/jadyer/article/details/12172839 -->
- /static/**=anon
- /verifyImage=anon
- <!-- perms[user:query]表示访问此连接需要权限为user:query的用户 -->
- /user=perms[query]
- <!-- roles[manager]表示访问此连接需要用户的角色为manager -->
- /user/add=roles[manager]
- /user/del/**=roles[admin]
- /user/edit/**=roles[manager]
- <!--所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过登录验证,如果未登录则跳到/login-->
- /** = authc
- </value>
- </property>
- </bean>
- <!-- 数据库保存的密码是使用MD5算法加密的,所以这里需要配置一个密码匹配对象 -->
- <!--<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.Md5CredentialsMatcher"></bean>-->
- <bean id="credentialsMatcher" class="com.ken.shiro.RetryLimitHashedCredentialsMatcher">
- <constructor-arg ref="cacheManager"></constructor-arg>
- <property name="hashAlgorithmName" value="md5"></property><!--加密算法为md5-->
- <property name="hashIterations" value="3"></property><!--3次md5迭代-->
- <!--是否存储散列后的密码为16进制,需要和生成密码时的一样,默认是base64-->
- <property name="storedCredentialsHexEncoded" value="true"></property>
- </bean>
- <!-- 配置 Bean 后置处理器: 会自动的调用和 Spring 整合后各个组件的生命周期方法. -->
- <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
- <!--缓存管理-->
- <!--<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.MemoryConstrainedCacheManager"></bean>-->
- <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager" >
- <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property>
- </bean>
- </beans>
Shiro自定义Realm时用注解的方式注入父类的credentialsMatcher的更多相关文章
- shiro自定义realm支持MD5算法认证(六)
1.1 散列算法 通常需要对密码 进行散列,常用的有md5.sha, 对md5密码,如果知道散列后的值可以通过穷举算法,得到md5密码对应的明文. 建议对md5进行散列时加salt(盐),进行 ...
- 权限框架 - shiro 自定义realm
上篇文章中是使用的默认realm来实现的简单登录,这仅仅只是个demo,真正项目中使用肯定是需要连接数据库的 首先创建自定义realm文件,如下: 在shiro中注入自定义realm的完全限定类名: ...
- shiro自定义Realm
1.1 自定义Realm 上边的程序使用的是shiro自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm. ...
- shiro自定义realm认证(五)
上一节介绍了realm的作用: realm:需要根据token中的身份信息去查询数据库(入门程序使用ini配置文件),如果查到用户返回认证信息,如果查询不到返回null.token就相当于是对用户输入 ...
- (3)shiro自定义realm
上面一章说到shiro的认证和授权最底层就是调用realm的getAuthorizationInfo(获取用户的角色和资源)和getAuthenticationInfo(校验账号密码是否正确)两个方法 ...
- Shiro自定义realm实现密码验证及登录、密码加密注册、修改密码的验证
一:先从登录开始,直接看代码 @RequestMapping(value="dologin",method = {RequestMethod.GET, RequestMethod. ...
- 将spring管理的bean使用注解的方式注入到servlet中
Filter和Servlet中不能直接注解使用spring的bean,因为这两个都是servlet容器维护管理的,当然也有实现方法,如下: 1.创建一个AbstractServlet 抽象类,让你的所 ...
- spring使用注解通过子类注入父类的私有变量
方法一 通过 super.setBaseDao方法设置父类私有变量 父类 public class BaseServiceImpl { private BaseDao baseDao; publ ...
- Java-Shiro(五):Shiro Realm讲解(二)IniRealm的用法、JdbcRelam的用法、自定义Realm
引入 上一篇在讲解Realm简介时,介绍过Realm包含大概4类缺省的Realm,本章主要讲解: 1)IniRealm的用法: 2)JdbcRealm基于mysql 默认表及查询语句实现认证.授权 ...
随机推荐
- 关于click的多次触发问题(冒泡事件)
1. 问题描述: 在点击事件触发时调用接口,若用户多次点击会造成多次调用接口,有时会引起一些数据错误的问题,如支付页面,点击多次时会在后台生成多个相同订单 解决方法: (1)加flag,让点击事件只执 ...
- seaweedFS
那首先我们来分析一下seaweedfs是什么?seaweedfs是一个非常优秀的由 golang 开发的分布式存储开源项目.它是用来存储文件的系统,并且与使用的语言无关,使得文件储存在云端变得非常方便 ...
- MongoDB\BSON\UTCDateTime::toDateTime
示例# 1 MongoDB \ BSON \ UTCDatetime:toDateTime()例子 <?php $utcdatetime = new MongoDB\BSON\UTCDateTi ...
- 14. Longest Common Prefix C++
采用纵向遍历,即对第一个字符串,取出第一个字符,检查是否出现在随后每一个字符串中,以此类推.当遍历完成或有一个字符串不符合要求,直接return. class Solution { public: s ...
- 牛客第二场A-run
链接:https://www.nowcoder.com/acm/contest/140/A 来源:牛客网 White Cloud is exercising in the playground. Wh ...
- ActiveMQ 到底是推还是拉?
http://activemq.apache.org/destination-options.html 1. consumer 的配置参数如下图: 配置consumer的示例: public void ...
- springboot程序构建一个docker镜像(十一)
准备工作 环境: linux环境或mac,不要用windows jdk 8 maven 3.0 docker 对docker一无所知的看docker教程. 创建一个springboot工程 引入web ...
- excle
1.固定某行列 如果要使一行不动,将光标定位于A2单击中,单击菜单"窗口----冻结窗格" 一行一列的,光标定位于B2单元格中,其它的以此类推 2.自动排序号 自动排序号,就是在某 ...
- 8188EU 在AM335X MC183上以AP+STA工作
[目的] 8188EU 在AM335X MC183上以AP+STA工作. [环境] 1. Ubuntu 16.04发行版 2. linux-3.2.0-psp04.06.00.11 3. MC1 ...
- java⑨
do-while,先执行一次,再判断! do{ 循环体 }while(循环条件); 经典案例: 1. 需求: 01.记录每次用户购买的商品金额! 之后进行 结账! 02.增加购买商品的数量 ...