1.shiro介绍
1.1什么是shiro
Shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架。
Spring security
1.2为什么要学shiro
既然shiro将安全认证相关的功能抽取出来组成一个框架,使用shiro就可以非常快速的完成认证、授权等功能的开发,降低系统成本。
shiro使用广泛,shiro可以运行在web应用,非web应用,集群分布式应用中越来越多的用户开始使用shiro。
java领域中spring security(原名Acegi)也是一个开源的权限管理框架,但是spring security依赖spring运行,而shiro就相对独立,最主要是因为shiro使用简单、灵活,所以现在越来越多的用户选择shiro。
1.3Shiro架构
1.3.1Subject
Subject即主体,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权
1.3.2SecurityManager
SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。
SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。
1.3.3Authenticator [ɔ:'θentɪkeɪtə]
Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。
1.3.4Authorizer
Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。
1.3.5realm
Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。
注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。
1.3.6sessionManager
sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录(SSO)。
1.3.7SessionDAO
SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。
1.3.8CacheManager
CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。
1.3.9Cryptography [krɪpˈɑ:grəfi]
Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。
2.shiro认证
2.1认证流程
2.2自定义Realm
上边的程序使用的是Shiro自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm
2.2.1shiro提供的realm
最基础的是Realm接口,CachingRealm负责缓存处理,AuthenticationRealm负责认证,AuthorizingRealm负责授权,通常自定义的realm继承AuthorizingRealm。
2.3散列算法
散列算法一般用于生成一段文本的摘要信息,散列算法不可逆,将内容可以生成摘要,无法将摘要转成原始内容。散列算法常用于对密码进行散列,常用的散列算法有MD5、SHA。
一般散列算法需要提供一个salt(盐)与原始内容生成摘要信息,这样做的目的是为了安全性,比如:111111的md5值是:96e79218965eb72c92a549dd5a330112,拿着“96e79218965eb72c92a549dd5a330112”去md5破解网站很容易进行破解,如果要是对111111和salt(盐,一个随机数)进行散列,这样虽然密码都是111111,
但是加不同的盐会生成不同的散列值。
2.3.1例子
@Test
public void tess(){
//md5加密,不加盐
String password_md5 = new Md5Hash("111111").toString();
System.out.println("md5加密,不加盐="+password_md5);
//md5加密,加盐,一次散列
String password_md5_sale_1 = new Md5Hash("111111", "zzsxt", 1).toString();
System.out.println("password_md5_sale_1="+password_md5_sale_1);
String password_md5_sale_2 = new Md5Hash("111111", "zzsxt", 2).toString();
System.out.println("password_md5_sale_2="+password_md5_sale_2);
//两次散列相当于md5(md5())
//使用SimpleHash
String simpleHash = new SimpleHash("MD5", "111111", "eteokues",1).toString();
System.out.println(simpleHash);
}
2.3.2在realm中使用
实际应用是将盐和散列后的值存在数据库中,自动realm从数据库取出盐和加密后的值由shiro完成密码校验。
2.3.2.1自定义realm
- 继承AuthorizingRealm
- 从token中获取用户的身份信息
- 根据身份信息从数据库获取用户的凭证信息
- 根据身份信息从数据库获取用户的salt
- 将token中用户的身份信息和数据库中用户的凭证信息封装到SimpleAuthenticationInfo实例中
- 返回简单认证信息类的实例simpleAuthenticationInfo//123456 加盐 ak47 691cc147ec1e00c3295e1f30bfb142a3
public class ShiroHashRelam extends AuthorizingRealm {
/**
* 认证
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
//1、继承AuthorizingRealm
//2、从token中获取用户的身份信息
//3、根据身份信息从数据库获取用户的凭证信息
//4、将token中用户的身份信息和数据库中用户的凭证信息封装到SimpleAuthenticationInfo实例中
//5、返回简单认证信息类的实例,使用ByteSource加盐 simpleAuthenticationInfo
String username = (String) token.getPrincipal();
//模拟从数据库获取用户凭证信息(password)和salt
String password = "691cc147ec1e00c3295e1f30bfb142a3";
String salt = "ak47";
SimpleAuthenticationInfo simpleAuthenticationInfo =
new SimpleAuthenticationInfo(username, "691cc147ec1e00c3295e1f30bfb142a3", ByteSource.Util.bytes(salt),this.getName());
return simpleAuthenticationInfo;
}
/**
* 授权
* @param principal
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
return null;
}
}
3.shiro授权
3.1授权流程
3.2授权方式
Shiro 支持三种方式的授权:
3.2.1编程式:通过写if/else 授权代码块完成:
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}
3.2.2注解式:通过在执行的Java方法上放置相应的注解完成:
@RequiresRoles("admin")
public void hello() {
//有权限
}
3.2.3JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成:
<shiro:hasRole name="admin">
<!— 有权限—>
</shiro:hasRole>
本教程序授权测试使用第一种编程方式,实际与web系统集成使用后两种方式。
4.Shiro中的方法
4.1shiro过虑器
过滤器简称 | 对应的java类 |
anon | org.apache.shiro.web.filter.authc.AnonymousFilter |
authc | org.apache.shiro.web.filter.authc.FormAuthenticationFilter |
authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter |
perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter |
port | org.apache.shiro.web.filter.authz.PortFilter |
rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter |
roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter |
ssl | org.apache.shiro.web.filter.authz.SslFilter |
user | org.apache.shiro.web.filter.authc.UserFilter |
logout | org.apache.shiro.web.filter.authc.LogoutFilter |
anon:例子/admins/**=anon 没有参数,表示可以匿名使用。
authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数
注意:anon,authcBasic,auchc,user是认证过滤器
perms,roles,ssl,rest,port是授权过滤器
4.2shiro的jsp标签
Jsp页面添加:
<%@ tagliburi="http://shiro.apache.org/tags" prefix="shiro" %>
标签名称 | 标签条件(均是显示标签内容) |
<shiro:authenticated/> | 登录之后 |
<shiro:notAuthenticated/> | 不在登录状态时 |
<shiro:guest/> | 用户在没有RememberMe时 |
<shiro:user/> | 用户在RememberMe时 |
<shiro:hasAnyRoles name="abc,123"/ > | 在有abc或者123角色时 |
<shiro:hasRole name="abc"/> | 拥有角色abc |
<shiro:lacksRole name="abc"/> | 没有角色abc |
<shiro:hasPermission name="abc"/> | 拥有权限资源abc |
<shiro:lacksPermission name="abc"/> | 没有abc权限资源 |
<shiro:principal/> | 显示用户身份名称 |
<shiro:principal property="username"/> 显示用户身份中的属性
附:URL匹配模式