shiro介绍
1、什么是shiro
shiro是Apache的一个开源框架,一个权限管理的框架,实现用户认证、用户授权。不仅可以实现web应用的权限管理,还可以实现c/s系统,分布式系统权限管理,shiro属于轻量级框架。
2、shiro架构
如图可知:shiro架构分为几个部分:
subject:主体,可以是用户或者程序,主体是访问系统,系统需要对主体进行认证、授权。
securityManager:安全管理器,主体进行认证和授权都是通过securityManager进行。
authenticator:认证器,主体进行认证最终通过authenticator进行的。
authorizer:授权器,主体进行授权最终通过authorizer进行的。
sessionManager:web应用中一般是用web容器对session进行管理,shiro也提供一套session管理方法。
cache Manager:缓存管理器,主要对session和授权数据进行缓存,比如将授权数据通过cache Manager进行缓存管理,和redis整合对缓存数据进行管理。
realm:域,领域,相当于是数据源,通过realm存取认证、授权相关数据。
注意:在realm中存储授权和认证的逻辑。
cryptography:密码管理,提供了一套加密/加密的组件,方便开发。比如提供常用的散列,加解密等功能。md5
3、shiro的jar包(一下是maven项目的依赖形式)
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-quartz</artifactId>
<version>1.2.3</version>
</dependency>
也可以通过引入shiro-all包括shiro所有的包:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.2.3</version>
</dependency>
4、shiro认证流程
5、shiro入门程序
1、jar包
shiro-core-1.2.3.jar
2、测试程序
(1)ini文件的形式进行处理,在项目下创建config/shiro-1.ini文件
内容:
[users]
zhangsan=123
lisi=1233
(2)代码
package com.zhou.dado.shiro1;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.apache.shiro.mgt.SecurityManager;
import org.junit.Test;
/**
* 认证代码
*/
public class Test1 {
@Test
public void testLoginLogout(){
//构建SecurityManager工厂,IniSecurityManagerFactory可以从ini文件中初始化SecurityManager环境
Factory<SecurityManager> factory=new IniSecurityManagerFactory("config/shiro-1.ini");
//创建SecurityManager
SecurityManager securityManager=factory.getInstance();
//将SecurityManager设置当前的运行环境中
SecurityUtils.setSecurityManager(securityManager);
//从SecurityUtils里面创建一个subject
Subject subject=SecurityUtils.getSubject();
//在认证提交前准备token(令牌),这里的账号和密码,将来是用户输入进去
UsernamePasswordToken token=new UsernamePasswordToken("zhangsan","123");
//执行认证提交
subject.login(token);
boolean isAuthenticated=subject.isAuthenticated();
System.out.println("是都认证通过:"+isAuthenticated);
}
}
(3)认证流程
1、创建token令牌,token中有用户提交的认证信息即账号和密码
2、执行subject.login(tocken),最终由securityManager通过Authenticator进行认证
3、Authenticator的实现ModularRealmAuthenticator调用realm从ini配置文件取用户真实的账号和密码,这里使用的是Inirealm(shiro自带)
4、Inirealm先根据token中的账号去ini中找该账号,如果找不到则给
ModularRealmAuthenticator返回null,如果找到则匹配密码,匹配密码成功则认证通过。
6、自定义realm
上边的程序使用的是shiro自带的inirealm,从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所有需要自定义realm。
1、shiro提供的realm
最基础的是realm接口,CachingRealm负责缓存处理,AuthenticationRealm负责认证,AuthorizingRealm负责授权,通常自定义的realm继承AuthorizingRealm。
2、自定义realm
package com.zhou.dado.shiro1;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class Test2_Realm extends AuthorizingRealm {
@Override
public String getName(){
return "Test2_Realm";
}
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
/**
* 认证
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//从token中获取和用户身份信息
String username=(String)token.getPrincipal();
//拿username从数据库中查询
//....
//如果查询不到则返回null
if(!username.equals("zhangsan")){//这里模拟查询不到
return null;
}
//获取从数据库查询出来的用户密码
String password="123";//这里使用静态数据模拟
//返回认证信息由父类AuthenticatingRealm进行认证
SimpleAuthenticationInfo simpleAuthenticationInfo=new SimpleAuthenticationInfo(username,password,getName());
return simpleAuthenticationInfo;
}
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
@Test
public void testLoginLogoutRealm(){
//构建SecurityManager工厂,IniSecurityManagerFactory可以从ini文件中初始化SecurityManager环境
Factory<SecurityManager> factory=new IniSecurityManagerFactory("config/shiro-realm.ini");
//创建SecurityManager
SecurityManager securityManager=factory.getInstance();
//将SecurityManager设置当前的运行环境中
SecurityUtils.setSecurityManager(securityManager);
//从SecurityUtils里面创建一个subject
Subject subject=SecurityUtils.getSubject();
//在认证提交前准备token(令牌),这里的账号和密码,将来是用户输入进去
UsernamePasswordToken token=new UsernamePasswordToken("zhangsan","123");
//执行认证提交
subject.login(token);
boolean isAuthenticated=subject.isAuthenticated();
System.out.println("是都认证通过:"+isAuthenticated);
}
ini配置:config/shiro-realm.ini
[main]
#自定义 realm
customRealm=com.zhou.dado.shiro1.Test2_Realm
#将realm设置到securityManager
securityManager.realms=$customRealm
7、散列算法
散列算法一般用于生成一段文本的摘要信息,散列算法不可逆,将内容可以生成摘要,无法将摘要转换成原始内容。散列算法常用语对密码进行散列,常用的散列算法有md5,sha。一般散列算法需要提供一个salt(盐)与原始内容生成摘要信息。这样做的目的是为了安全性,比如111111的md5值是96e79218965eb72c92a549dd5a330112,拿着96e79218965eb72c92a549dd5a330112去md5破解网站和容易进行破解,如果要是对11111的salt(盐,一个随机数)进行散列,这样虽然密码都是111111加不同的盐会生成不同的散列值。
例子:
//md5加密,不加盐
String password_md5 = new Md5Hash("111111").toString();
System.out.println("md5加密,不加盐="+password_md5);
//md5加密,加盐,一次散列
String password_md5_sale_1 = new Md5Hash("111111", "eteokues", 1).toString();
System.out.println("password_md5_sale_1="+password_md5_sale_1);
String password_md5_sale_2 = new Md5Hash("111111", "uiwueylm", 1).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);
在realm中使用:
package com.zhou.dado.shiro1;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
public class Test2_RealmMd5 extends AuthorizingRealm {
@Override
public String getName(){
return "Test2_Realm";
}
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
/**
* 认证
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//从token中获取和用户身份信息
String username=(String)token.getPrincipal();
//根据用户账号从数据库取出盐和加密后的值,这里是静态数据,如果账号没有找到用户信息则返回null,shiro抛出异常“账号不存在”
//按照固定规则加密码结果,此密码要在数据库存储,原始密码是111111,盐eteokues
String password="cb571f7bd7a6f73ab004a70322b963d5";//这里使用静态数据模拟
String salt="eteokues";
//返回认证信息由父类AuthenticatingRealm进行认证
SimpleAuthenticationInfo simpleAuthenticationInfo=new SimpleAuthenticationInfo(username,password, ByteSource.Util.bytes(salt),getName());
return simpleAuthenticationInfo;
}
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
realm配置:
[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次数
credentialsMatcher.hashIterations=1
#将凭证匹配器设置到realm
customRealm=com.zhou.dado.shiro1.Test2_Realm
customRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$customRealm