http://blog.csdn.net/facekbook/article/details/54906635

shiro认证

本文介绍shiro的认证功能

  • 认证流程
  • 入门程序(用户登录和退出)
  • 自定义Realm
  • 散列算法

认证流程

开始构造SecurityManager环境subject.login();提交认证securityManager.login()执行认证Authenticator执行认证Realm根据身份获取验证信息结束

入门程序(用户登录和退出)

创建Java项目

jdk版本:1.7.0_67

加入shiro的jar包以及依赖包

log4j.properties日志文件配置

log4j.rootLogger=debug, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n

eclipse中ini文件打开方式配置

shiro使用ini文件作为配置文件。所以需要修改eclipse中ini文件的打开方式,默认的话ini文件是使用记事本打开。具体如下图: 

创建ini配置文件

在classpath路径下创建shiro-first.ini文件,文件内容是测试用户的账号和密码。内容如下:

[users]
zhangsan=123
lisi=456

认证代码

@Test
public void testLoginAndLogOut() {
// 构建SecurityManager工厂,IniSecurityManagerFactory可以从ini文件中初始化SecurityManager环境
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-first.ini");
// 通过工厂创建SecurityManager
SecurityManager securityManager = factory.getInstance();
// 将SecurityManager设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//创建一个Subject实例,该实例认证需要使用上面创建的SecurityManager
Subject subject = SecurityUtils.getSubject();
//创建token令牌,账号和密码是ini文件中配置的
AuthenticationToken token = new UsernamePasswordToken("zhangsan", "123");
try {
//用户登录
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
}
//用户认证状态
Boolean isAuthenticated = subject.isAuthenticated();
System.out.println("用户认证状态:"+isAuthenticated);//输出true //用户退出
subject.logout(); isAuthenticated = subject.isAuthenticated();
System.out.println("用户认证状态:"+isAuthenticated);//输出false
}

认证执行流程

1.创建token令牌,token中有用户提交的认证信息即账号和密码。 
2.执行subject.login(token),最终由securityManager通过 Authenticator进行认证。 
3.Authenticator的实现ModuleRealmAuthenticator调用realm从init文件读取用户真实的账号和密码,这里使用的是IniRealm(Shiro自带) 
4.IniRealm先根据token中的账号去ini中找该账号,如果找不到则给ModuleRealmAuthenticator返回null,如果找到则匹配密码,匹配密码成功则认证通过。

常见的异常

  • UnknownAccountException 
    账号不存在异常如下:
org.apache.shiro.authc.UnknownAccountException: Realm [org.apache.shiro.realm.text.IniRealm@9cdc393] was unable to find account data for the submitted AuthenticationToken [org.apache.shiro.authc.UsernamePasswordToken - zhangsan1
  • IncorrectCredentialsException 
    当输入密码错误会抛出此异常,如下:
org.apache.shiro.authc.IncorrectCredentialsException: Submitted credentials for token [org.apache.shiro.authc.UsernamePasswordToken - zhangsan, rememberMe=false] did not match the expected credentials.

更多异常信息如下: 
DisabledAccountException(帐号被禁用) 
LockedAccountException(帐号被锁定) 
ExcessiveAttemptsException(登录失败次数过多) 
ExpiredCredentialsException(凭证过期)等 
类结构如下图 

自定义Realm

上面的程序使用的是Shiro自带的IniRealm,IniRealm从ini配置文件中读取用户的信息。但是实际情况中大部分情况下是从数据库中获取用户信息,所以需要自定义realm。

Shiro中Realm

 
最基础的是Realm接口,CachingRealm负责缓存管理,AuthenticatingRealm负责认证,AuthorizingRealm负责授权,通常自定义的Realm继承AuthorizingRealm。

自定义Realm代码

通过继承AuthorizingRealm类实现

public class CustomRealm extends AuthorizingRealm {

    /**
* 认证方法
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 从token中获取用户身份信息
String username = (String) token.getPrincipal(); // 正常逻辑应该是通过username查询数据库。
// 如果查询不到返回null
if (!"zhangsan".equals(username)) {// 这里模仿查询不到
return null;
} // 模拟从数据获取密码
String password = "123"; // 返回认证信息交由父类AuthorizingRealm认证
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, password, ""); return authenticationInfo;
} /**
* 授权方法
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
}

ini配置文件

新建shiro-realm.ini文件。内容如下:

[main]
#自定义realm
customRealm=com.knight.shiro.realm.CustomRealm
#将realm设置到securityManager
securityManager.realm=$customRealm

这里不需要配置users,是因为我们这里模拟users的获取来自数据库。

测试代码

@Test
public void testCustomeRealm() {
// 构建SecurityManager工厂,IniSecurityManagerFactory可以从ini文件中初始化SecurityManager环境
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-realm.ini");
// 通过工厂创建SecurityManager
SecurityManager securityManager = factory.getInstance();
// 将SecurityManager设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//创建一个Subject实例,该实例认证需要使用上面创建的SecurityManager
Subject subject = SecurityUtils.getSubject();
//创建token令牌,账号和密码是ini文件中配置的
//AuthenticationToken token = new UsernamePasswordToken("zhangsan", "123");//账号密码正确token
//AuthenticationToken token = new UsernamePasswordToken("zhangsan", "1234");//密码错误异常token
AuthenticationToken token = new UsernamePasswordToken("zhangsan1", "123");//账号错误异常token
try {
//用户登录
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
}
//用户认证状态
Boolean isAuthenticated = subject.isAuthenticated();
System.out.println("用户认证状态:"+isAuthenticated);//输出true
}

散列算法

散列算法一般用于生成一段文本的摘要信息,散列算法不可逆,也就是将内容生成摘要,但是反过来通过摘要生成内容是不可以的。散列算法常用于对密码进行散列,常用的散列算法有MD5、SHA。一般散列算法需要提供一个salt(盐)与原始内容生成摘要,这样做的目的是为了安全性。

例子

        Md5Hash  md5Hash = new Md5Hash("111111");
System.out.println("md5加密,不加盐:"+md5Hash.toString()); //md5加密,加盐,一次hash
String password_md5_sale_1 = new Md5Hash("11111", "aga23", 1).toString();
System.out.println("md5加密,加盐,一次hash:"+password_md5_sale_1); //md5加密,加盐,两次hash
String password_md5_sale_2 = new Md5Hash("11111", "aga23", 2).toString();
System.out.println("md5加密,加盐,两次hash:"+password_md5_sale_2);//相当于md5(md5('1111')) //使用simpleHash
String simpleHash = new SimpleHash("MD5", "11111", "aga23", 1).toString();
System.out.println(simpleHash);

在realm中使用散列算法

实际系统中是将盐和散列后的值存储在数据库中,自定义realm从数据库取出盐和加密后的值由shiro完成密码校验。

自定义支持散列的realm

public class CustomRealmMd5 extends AuthorizingRealm {

    /**
* 认证方法
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 从token中获取用户身份信息
String username = (String) token.getPrincipal(); // 正常逻辑应该是通过username查询数据库。
// 如果查询不到返回null
if (!"zhangsan".equals(username)) {// 这里模仿查询不到
return null;
} // 模拟从数据获取密码
String password = "fdf907b0d3f427b9ffa2f86f213d1afd";
// 盐
String salt = "aga23";
// 返回认证信息交由父类AuthorizingRealm认证
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, password,
ByteSource.Util.bytes(salt), ""); return authenticationInfo;
} /**
* 授权方法
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
}

支持散列的realm配置

[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#设置散列算法
credentialsMatcher.hashAlgorithmName=md5
#设置散列次数
credentialsMatcher.hashIterations=1 #将凭证匹配器设置到realm
customRealm=com.knight.shiro.realm.CustomRealmMd5
customRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$customRealm

测试代码

注意修改配置文件的路径

    @Test
public void testCustomeRealmMd5() {
// 构建SecurityManager工厂,IniSecurityManagerFactory可以从ini文件中初始化SecurityManager环境
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-realm-md5.ini");
// 通过工厂创建SecurityManager
SecurityManager securityManager = factory.getInstance();
// 将SecurityManager设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//创建一个Subject实例,该实例认证需要使用上面创建的SecurityManager
Subject subject = SecurityUtils.getSubject();
//创建token令牌,账号和密码是ini文件中配置的
//AuthenticationToken token = new UsernamePasswordToken("zhangsan", "11111");//账号密码正确token
AuthenticationToken token = new UsernamePasswordToken("zhangsan", "1234");//密码错误异常token
//AuthenticationToken token = new UsernamePasswordToken("zhangsan1", "11111");//账号错误异常token
try {
//用户登录
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
}
//用户认证状态
Boolean isAuthenticated = subject.isAuthenticated();
System.out.println("用户认证状态:"+isAuthenticated);//输出true
}

该文章涉及的代码

代码地址

(转) shiro权限框架详解04-shiro认证的更多相关文章

  1. (转) shiro权限框架详解06-shiro与web项目整合(上)

    http://blog.csdn.net/facekbook/article/details/54947730 shiro和web项目整合,实现类似真实项目的应用 本文中使用的项目架构是springM ...

  2. (转)shiro权限框架详解03-shiro介绍

    http://blog.csdn.net/facekbook/article/details/54893740 shiro介绍 本文正式进入主题.本文将介绍如下内容: 什么是shiro 为什么需要学习 ...

  3. (转)shiro权限框架详解06-shiro与web项目整合(下)

    http://blog.csdn.net/facekbook/article/details/54962975 shiro和web项目整合,实现类似真实项目的应用 web项目中认证 web项目中授权 ...

  4. (转)shiro权限框架详解05-shiro授权

    http://blog.csdn.net/facekbook/article/details/54910606 本文介绍 授权流程 授权方式 授权测试 自定义授权realm 授权流程 开始构造Secu ...

  5. (转)shiro权限框架详解02-权限理论介绍

    http://blog.csdn.net/facekbook/article/details/54893042 权限管理解决方案 本文主要介绍权限管理的解决方法: 粗颗粒度和细颗粒度 基于url拦截 ...

  6. (转)shiro权限框架详解01-权限理论介绍

    http://blog.csdn.net/facekbook/article/details/54890365 权限管理 本文介绍权限管理的理论和权限管理的一些名词. 介绍权限管理 理解身份认证和授权 ...

  7. Shiro 安全框架详解二(概念+权限案例实现)

    Shiro 安全框架详解二 总结内容 一.登录认证 二.Shiro 授权 1. 概念 2. 授权流程图 三.基于 ini 的授权认证案例实现 1. 实现原理图 2. 实现代码 2.1 添加 maven ...

  8. Shiro 安全框架详解一(概念+登录案例实现)

    shiro 安全框架详细教程 总结内容 一.RBAC 的概念 二.两种常用的权限管理框架 1. Apache Shiro 2. Spring Security 3. Shiro 和 Spring Se ...

  9. Shiro权限管理框架详解

    1 权限管理1.1 什么是权限管理 基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被 ...

随机推荐

  1. Windows下Jupyter notebook 修改默认打开(工作、保存)文件夹(路径)

    今天晚上兴致一起突然想看看我写了那么多的ipynb文件都去哪了 首先查了一下,应该是都默认保存到    C:\Users\芩溪儿   路径下了 然后我就想,我是不是得改改啊,总在那跟别的文件夹在一起总 ...

  2. 9day条件语句和基本数据类型

    1基本数据类型: 字符串‘’," ",''' ''' 单引号,双引号,三引号 加法: n1='ruser' n2='sb' n3=n1+n2 print(n3) 乘法: n1='r ...

  3. NFS实时备份

    方法一(inotify+rsync): 1.安装inotify-tools(客户端)[监听工具],实现数据属实备份检查目录是否有如下文档,没有表示操作系统不支持 ls -l /proc/sys/fs/ ...

  4. Python中字符串操作函数string.split('str1')和string.join(ls)

    Python中的字符串操作函数split 和 join能够实现字符串和列表之间的简单转换, 使用 .split()可以将字符串中特定部分以多个字符的形式,存储成列表 def split(self, * ...

  5. flask-sqlalchemy 配置 mysql (转载的文章)

    一.当然是把必备的包给安装上才行: Flask-SQLAlchemy pip install flask-sqlalchemy MySQL windows下64位压缩包的安装方式可以参考: http: ...

  6. 通过反射,对javabean属性进行过滤操作

    /** * 根据属性名获取属性值 * @param fieldName 属性名 * @param o 传入对象 * @return */ private Object getFieldValueByN ...

  7. 《编程导论(Java)&#183;2.1.2 啊,我看到了多态》-什么是多态(polymorphism)

    1.不明觉厉 很多人学习多态时,会认为. 之所以不明觉厉,由于多态的定义:事物存在的多种表现形态:而后,有人将重载(overload).改写(override).多态变量和泛型归结于同一个术语&quo ...

  8. 在Linux中samba server的配置

    1.查看是否安装samba服务 # rpm –qa |grep samba 2.若没安装,则安 # yum install samba 执行4次此命令 3.查看安装的samba文件 #rpm–qa | ...

  9. C++研究之在开发中你可能没有考虑到的两个性能优化

     1:多余的存储引用导致性能减少. 2:利用局部性提高程序性能: 先来说说引用是怎么减少程序性能.个人觉得减少程序性能主要有两个原因,一是数据结构选择不合理,二是多层嵌套循环导致部分代码被多余反复 ...

  10. linux下安装rar解压包

    直接解压时出现的问题如下 原因:使用rar命令需要安装WinRAR 1.在本机下载好解压,然后将解压包拖到linux上 2.进行安装,在rar目录想直接make