shiro的使用2 灵活使用shiro的密码服务模块
shiro最闪亮的四大特征是认证,授权,加密,会话管理。
上一篇已经演示了如何使用shiro的授权模块,有了shiro这个利器,可以以统一的编码方式对用户的登入,登出,认证进行管理,相当的优雅。
为了提高应用系统的安全性,这里主要关注shiro提供的密码服务模块;
1,加密工具类的熟悉
首先来个结构图,看看shiro哥哥提供了哪些加密工具类:

为此,写了一个工具类来探测和熟悉这些工具类的使用:
|
package com.util; |
2,一个综合点的例子,配置帐号的密码生成方式,并利用ehcache,设定输错密码多少次,用户被锁定一个小时;
1,提供一个ehcache的简单实用类
| package com.util.cache; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Element; import net.sf.ehcache.config.CacheConfiguration; import net.sf.ehcache.store.MemoryStoreEvictionPolicy; /** * User: cutter.li * Date: 2014/6/30 0030 * Time: 15:32 * 备注: ehcache的缓存工具类 */ public final class EhcacheUtil { private static final CacheManager cacheManager = CacheManager.getInstance(); /** * 创建ehcache缓存,创建之后的有效期是1小时 */ private static Cache cache = new Cache(new CacheConfiguration("systemCache", 5000).memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.FIFO).timeoutMillis(300).timeToLiveSeconds( 60 * 60)); static { cacheManager.addCache(cache); } public static void putItem(String key, Object item) { if (cache.get(key) != null) { cache.remove(key); } Element element = new Element(key, item); cache.put(element); } public static void removeItem(String key) { cache.remove(key); } public static void updateItem(String key, Object value) { putItem(key, value); } public static Object getItem(String key) { Element element= cache.get(key); if(null!=element) { return element.getObjectValue(); } return null; } } |
2,提供加密和校验密文的方法
| /** * 对密码进行md5加密,并返回密文和salt,包含在User对象中 * @param username 用户名 * @param password 密码 * @return 密文和salt */ public static User md5Password(String username,String password){ Preconditions.checkArgument(!Strings.isNullOrEmpty(username),"username不能为空"); Preconditions.checkArgument(!Strings.isNullOrEmpty(password),"password不能为空"); SecureRandomNumberGenerator secureRandomNumberGenerator=new SecureRandomNumberGenerator(); String salt= secureRandomNumberGenerator.nextBytes().toHex(); //组合username,两次迭代,对密码进行加密 String password_cipherText= new Md5Hash(password,username+salt,2).toHex(); User user=new User(); user.setPassword(password_cipherText); user.setSalt(salt); user.setUsername(username); return user; } /** * 通过username,password,salt,校验密文是否匹配 ,校验规则其实在配置文件中,这里为了清晰,写下来 * @param username 用户名 * @param password 原密码 * @param salt 盐 * @param md5cipherText 密文 * @return */ public static boolean checkMd5Password(String username,String password,String salt,String md5cipherText) { Preconditions.checkArgument(!Strings.isNullOrEmpty(username),"username不能为空"); Preconditions.checkArgument(!Strings.isNullOrEmpty(password),"password不能为空"); Preconditions.checkArgument(!Strings.isNullOrEmpty(md5cipherText),"md5cipherText不能为空"); //组合username,两次迭代,对密码进行加密 String password_cipherText= new Md5Hash(password,username+salt,2).toHex(); return md5cipherText.equals(password_cipherText); } |
3,配置认证的数据源使用的密码校验接口
| <bean id="myRealm" class="com.util.MysqlJdbcRealM"> <property name="credentialsMatcher" ref="passwordMatcher"></property> </bean> <bean id="passwordMatcher" class="com.util.LimitRetryHashedMatcher"> <property name="hashAlgorithmName" value="md5"></property> <property name="hashIterations" value="2"></property> <property name="storedCredentialsHexEncoded" value="true"></property> </bean> |
4,注册和登录方法的修改
| /** * 用户注册 * * @param entity * @return */ @Override public ResponseEntity<Map> createSubmit(User entity) { //加密用户输入的密码,得到密码的摘要和盐,保存到数据库 User user = EndecryptUtils.md5Password(entity.getUsername(), entity.getPassword()); entity.setPassword(user.getPassword()); entity.setSalt(user.getSalt()); Map<String, Object> map = Maps.newHashMap(); try { boolean createResult = service.modify(entity, OperationType.create); map.put("success", createResult); } catch (Exception e) { e.printStackTrace(); } return new ResponseEntity<Map>(map, HttpStatus.OK); } ------------------------------------------------------------------华丽的分割线--------------------------------------------------------------------------------------------------- @RequestMapping(value = "login", method = RequestMethod.POST) public ResponseEntity<Message> loginSubmit(String username, String password, String vcode, HttpServletRequest request) { message.setSuccess(); validateLogin(message, username, password, vcode); try { // String code = request.getSession().getAttribute(AppConstant.KAPTCHA_SESSION_KEY).toString(); // if (!vcode.equalsIgnoreCase(code)) { // message.setCode(AppConstant.VALIDCODE_ERROR); // message.setMsg("验证码错误"); // } if (message.isSuccess()) { Subject subject = SecurityUtils.getSubject(); subject.login(new UsernamePasswordToken(username, password,false)); if (subject.isAuthenticated()) { message.setMsg("登录成功"); } else { message.setCode(AppConstant.USERNAME_NOTEXIST); message.setMsg("用户名/密码错误"); } } }catch (ExcessiveAttemptsException ex) { message.setCode(AppConstant.USERNAME_NOTEXIST); message.setMsg("帐号被锁定1小时"); ex.printStackTrace(); } catch (AuthenticationException ex){ message.setCode(AppConstant.USERNAME_NOTEXIST); message.setMsg("用户名/密码错误"); ex.printStackTrace(); } finally { return new ResponseEntity<Message>(message, HttpStatus.OK); } } ---------------------------------------------------------------认证的修改------------------------------------------------------------------------------------- //登录认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token; String username = String.valueOf(usernamePasswordToken.getUsername()); User user = userService.findByUserName(username); SimpleAuthenticationInfo authenticationInfo = null; if (null != user) { String password = new String(usernamePasswordToken.getPassword()); //密码校验移交给了shiro的提供的一个接口实现类,所以这里注释掉 // if (EndecryptUtils.checkMd5Password(username,password,user.getSalt(),user.getPassword())) { authenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName()); authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(username+user.getSalt())); // } } return authenticationInfo; } |
5,重写密码校验的方法
| package com.util; import com.util.cache.EhcacheUtil; 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 java.util.concurrent.atomic.AtomicInteger; /** * User: cutter.li * Date: 2014/6/30 0030 * Time: 15:22 * 备注: 限制登录次数,如果5次出错,锁定1个小时 */ public class LimitRetryHashedMatcher extends HashedCredentialsMatcher { @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { String username = (String) token.getPrincipal(); //retrycount + 1 Object element = EhcacheUtil.getItem(username); if (element == null) { EhcacheUtil.putItem(username, 1); element=0; }else{ int count=Integer.parseInt(element.toString())+1; element=count; EhcacheUtil.putItem(username,element); } AtomicInteger retryCount = new AtomicInteger(Integer.parseInt(element.toString())); if (retryCount.incrementAndGet() > 5) { //if retrycount >5 throw throw new ExcessiveAttemptsException(); } boolean matches = super.doCredentialsMatch(token, info); if (matches) { //clear retrycount EhcacheUtil.removeItem(username); } return matches; } } |
6,搞定收工
连续输错5次密码之后,出现如下提示;

7,小结
通过封装常用的加密解密工具类,降低了对jdk自带密码工具类的学习成本;
可以灵活定义密码的生成和判断方式,并改变密码判断过程的逻辑;
shiro的使用2 灵活使用shiro的密码服务模块的更多相关文章
- 使用shiro的密码服务模块
http://jinnianshilongnian.iteye.com/blog/2021439 http://www.cnblogs.com/snidget/p/3817763.html
- 《跟我学Shiro》学习笔记 第一章:Shiro简介
前言 现在在学习Shiro,参照着张开涛老师的博客进行学习,然后自己写博客记录一下学习中的知识点,一来可以加深理解,二来以后遗忘了可以查阅.没有学习过Shiro的小伙伴,也可以和我一起学习,大家共同进 ...
- springboot+shiro+cas实现单点登录之shiro端搭建
github:https://github.com/peterowang/shiro-cas 本文如有配置问题,请查看之前的springboot集成shiro的文章 1.配置ehcache缓存,在re ...
- Apache Shiro 使用手册(三)Shiro 授权
授权即访问控制,它将判断用户在应用程序中对资源是否拥有相应的访问权限. 如,判断一个用户有查看页面的权限,编辑数据的权限,拥有某一按钮的权限,以及是否拥有打印的权限等等. 一.授权的三要素 授权有着三 ...
- Apache Shiro 使用手册(三)Shiro 授权(转发:http://kdboy.iteye.com/blog/1155450)
授权即访问控制,它将判断用户在应用程序中对资源是否拥有相应的访问权限. 如,判断一个用户有查看页面的权限,编辑数据的权限,拥有某一按钮的权限,以及是否拥有打印的权限等等. 一.授权的三要素 授权有着三 ...
- (转) Apache Shiro 使用手册(三)Shiro 授权
解惑之处: 使用冒号分隔的权限表达式是org.apache.shiro.authz.permission.WildcardPermission 默认支持的实现方式. 这里分别代表了 资源类型:操作:资 ...
- 第一章 Shiro简介——《跟我学Shiro》(转)
目录贴:跟我学Shiro目录贴 1.1 简介 Apache Shiro是Java的一个安全框架.目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可 ...
- Apache Shiro 使用手册(一)Shiro架构介绍 - kdboy - ITeye技术网站
转载 原文地址 http://kdboy.iteye.com/blog/1154644 一.什么是Shiro Apache Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理 ...
- Shiro 学习笔记(二)——shiro身份验证
身份验证: 在应用中证明他就是他本人.一般上用身份证.用户/密码 来证明. 在shiro中,用户需要提供principals (身份)和credentials(证明)给shiro,从而应用能验证用户身 ...
随机推荐
- 在Visual Studio Code中配置GO开发环境
一.GO语言安装 详情查看:GO语言下载.安装.配置 二.GoLang插件介绍 对于Visual Studio Code开发工具,有一款优秀的GoLang插件,它的主页为:https://github ...
- [数据结构]——链表(list)、队列(queue)和栈(stack)
在前面几篇博文中曾经提到链表(list).队列(queue)和(stack),为了更加系统化,这里统一介绍着三种数据结构及相应实现. 1)链表 首先回想一下基本的数据类型,当需要存储多个相同类型的数据 ...
- GJM : C#设计模式汇总整理——导航 【原创】
感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...
- UML图中经常用到几种的关系图例
学习这个东西挺奇怪的,时间一长就容易忘记,或者记不清楚.今天看到一些UML图的关系,发现有些出入了,索性就写下来,以后再忘记的时候过来看看. 在UML的类图中,常见的有以下几种关系: 继承(Gener ...
- 嵌入式&iOS:回调函数(C)与block(OC)回调对比
学了OC的block,再写C的回调函数有点别扭,对比下区别,回忆记录下. C的回调函数: callBack.h 1).定义一个回调函数的参数数量.类型. typedef void (*CallBack ...
- 信息安全-5:RSA算法详解(已编程实现)[原创]
转发注明出处:http://www.cnblogs.com/0zcl/p/6120389.html 背景介绍 1976年以前,所有的加密方法都是同一种模式: (1)甲方选择某一种加密规则,对信息进行加 ...
- 如何区别数据库删除语句drop与delete与truncate?
1.delete:删除数据表中的行(可以删除某一行,也可以在不删除数据表的情况下删除所有行) 删除某一行:delete from 数据表名称 where 列名称=值: 删除所有行:delete*fro ...
- #ifndef
关于c的#ifndef条件编译: 1)最好把头文件的内容都放在#ifndef和#endif中 2)一般格式: #ifndef <标识> #define <标识> ...... ...
- iOS开发系列--地图与定位
概览 现在很多社交.电商.团购应用都引入了地图和定位功能,似乎地图功能不再是地图应用和导航应用所特有的.的确,有了地图和定位功能确实让我们的生活更加丰富多彩,极大的改变了我们的生活方式.例如你到了一个 ...
- SQL开发技巧(二)
本系列文章旨在收集在开发过程中遇到的一些常用的SQL语句,然后整理归档,本系列文章基于SQLServer系列,且版本为SQLServer2005及以上-- 文章系列目录 SQL开发技巧(一) SQL开 ...