Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
官方网站:http://shiro.apache.org/
一、shiro中的认证
1、认证中的关键对象
- Subject:主体
访问系统的用户,主体可以是用户、程序等,进行认证的都被称为主体。 - Principal:身份信息
是主体进行身份认证的标识,标识必须具有唯一性,如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份。 - credential:凭证信息
是只有主体自己知道的安全信息,如密码、证书等。
2、简单认证的开发
创建项目并引入依赖。
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.6.0</version>
</dependency>
在resources中引入shiro配置文件,命名随意,后缀必须以.ini结尾。(例如:shiro.ini)
[users]
yzs=123456
创建测试认证类
package com.yzs;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
public class TestAuthenticator {
public static void main(String[] args) {
//1、创建安全管理器对象
DefaultSecurityManager securityManager=new DefaultSecurityManager();
//2、给安全管理器设置realms
securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
//3、SecurityUtils 给全局安全工具类设置安全管理器
SecurityUtils.setSecurityManager(securityManager);
//4、关键对象,主体
Subject subject = SecurityUtils.getSubject();
//5、创建令牌
UsernamePasswordToken token=new UsernamePasswordToken("yzs","12345678");
try{
System.out.println("认证状态:"+subject.isAuthenticated());
//用户登录认证
subject.login(token);
System.out.println("认证状态:"+subject.isAuthenticated());
}catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("认证失败:用户名不存在");
} catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("认证失败:密码错误");
}
}
}
3、实现自定义realm
创建自定义realm类
package realm;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class CustomerRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String principal = (String) authenticationToken.getPrincipal();
System.out.println(principal);
if ("zhangsan".equals(principal)){
return new SimpleAuthenticationInfo(principal,"123",this.getName());
}
return null;
}
}
创建测试类
package com.yzs;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import realm.CustomerRealm;
public class TestAuthenticatorCustomerRealm {
public static void main(String[] args) {
//1、创建安全管理器对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//2、给安全管理器设置自定义realm
securityManager.setRealm(new CustomerRealm());
//3、SecurityUtils 给全局安全工具类设置安全管理器
SecurityUtils.setSecurityManager(securityManager);
//4、关键对象,主体
Subject subject = SecurityUtils.getSubject();
//5、创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","123");
try {
subject.login(token);
System.out.println(subject.isAuthenticated());
}catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("认证失败:用户名不存在");
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("认证失败:密码错误");
}
}
}
4、使用md5+salt认证
md5认证常用方法
package com.yzs;
import org.apache.shiro.crypto.hash.Md5Hash;
public class TestShiroMd5 {
public static void main(String[] args) {
//使用set方法创建
Md5Hash md5Hash = new Md5Hash();
md5Hash.setBytes("123".getBytes());
System.out.println(md5Hash.toHex());
//使用md5构造方法创建
Md5Hash md5Hash1 = new Md5Hash("123");
System.out.println(md5Hash1.toHex());
//使用md5+salt处理
Md5Hash md5Hash2 = new Md5Hash("123","X0*7ps");
System.out.println(md5Hash2.toHex());
//使用md5+salt+hash散列处理
Md5Hash md5Hash3 = new Md5Hash("123","X0*7ps",1024);
System.out.println(md5Hash3.toHex());
}
}
创建自定义realm类
package realm;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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 CustomerMd5Realm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String principal = (String) authenticationToken.getPrincipal();
if ("zhangsan".equals(principal)){
return new SimpleAuthenticationInfo(principal,
"e4f9bf3e0c58f045e62c23c533fcf633",
ByteSource.Util.bytes("X0*7ps"),
this.getName());
}
return null;
}
}
创建测试类
package com.yzs;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import realm.CustomerMd5Realm;
public class TestCustomerMd5RealmAuthenicator {
public static void main(String[] args) {
//1、创建安全管理器对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//2、给安全管理器设置realm
CustomerMd5Realm realm = new CustomerMd5Realm();
//3、设置realm使用hash凭证匹配器
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//使用算法
hashedCredentialsMatcher.setHashAlgorithmName("md5");
//散列次数
hashedCredentialsMatcher.setHashIterations(1024);
realm.setCredentialsMatcher(hashedCredentialsMatcher);
securityManager.setRealm(realm);
//4、SecurityUtils 给全局安全工具类设置安全管理器
SecurityUtils.setSecurityManager(securityManager);
//5、关键对象,主体
Subject subject = SecurityUtils.getSubject();
//6、创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
try {
subject.login(token);
System.out.println(subject.isAuthenticated());
}catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("认证失败:用户名不存在");
}catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("认证失败:密码错误");
}
}
}
二、shiro中的授权
1、授权中的关键对象
- Subject:主体
主体需要访问系统中的资源。 - Resource:资源
如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型和资源实例,比如商品信息为资源类型,类型为to1的商品为资源实例,编号为001的商品信息也属于资源实例。 - Permission:权限/许可
规定了主体对资源的操作许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为001用户的修改权限等,通过权限可知主体对哪些资源都有哪些操作许可。
2、授权方式
- RBAC基于角色的访问控制(Role-Based Access Control)
- RBAC基于资源的访问控制(Resource-Based Access Control)
权限字符串规则:资源标识符:操作:资源实例标识符
3、编程实现方式
编程式
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole("admin")){
//有权限
}else{
//无权限
}
注解式
@RequiresRoles("admin")
public void hello(){
//有权限
}
标签式(适应于jsp页面)
//页面引入shiro标签
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<shiro:hasRole name="admin">
<!- 有权限 ->
</shiro:hasRole>
4、简单授权开发
创建自定义realm类
package realm;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
public class CustomerMd5Realm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
System.out.println("身份信息:"+primaryPrincipal);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//将数据库中查询角色信息赋值给权限对象
simpleAuthorizationInfo.addRole("admin");
simpleAuthorizationInfo.addRole("user");
//将数据库中查询权限信息赋值给权限对象
simpleAuthorizationInfo.addStringPermission("user:*:*");
simpleAuthorizationInfo.addStringPermission("product:*:01");
return simpleAuthorizationInfo;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String principal = (String) authenticationToken.getPrincipal();
if ("zhangsan".equals(principal)){
return new SimpleAuthenticationInfo(principal,
"e4f9bf3e0c58f045e62c23c533fcf633",
ByteSource.Util.bytes("X0*7ps"),
this.getName());
}
return null;
}
}
创建测试类
package com.yzs;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import realm.CustomerMd5Realm;
import java.util.Arrays;
public class TestCustomerMd5RealmAuthenicator {
public static void main(String[] args) {
//1、创建安全管理器对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//2、给安全管理器设置realm
CustomerMd5Realm realm = new CustomerMd5Realm();
//3、设置realm使用hash凭证匹配器
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//使用算法
hashedCredentialsMatcher.setHashAlgorithmName("md5");
//散列次数
hashedCredentialsMatcher.setHashIterations(1024);
realm.setCredentialsMatcher(hashedCredentialsMatcher);
securityManager.setRealm(realm);
//4、SecurityUtils 给全局安全工具类设置安全管理器
SecurityUtils.setSecurityManager(securityManager);
//5、关键对象,主体
Subject subject = SecurityUtils.getSubject();
//6、创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
try {
subject.login(token);
System.out.println(subject.isAuthenticated());
}catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("认证失败:用户名不存在");
}catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("认证失败:密码错误");
}
//授权
if (subject.isAuthenticated()){
//基于单角色权限控制
System.out.println(subject.hasRole("admin"));
//基于多角色权限控制
System.out.println(subject.hasAllRoles(Arrays.asList("admin","user")));
//是否具有其中一个角色
boolean[] booleans = subject.hasRoles(Arrays.asList("admin","user","super"));
for (boolean booleans1:booleans){
System.out.println(booleans1);
}
//基于权限字符串访问控制
System.out.println(subject.isPermitted("user:*:*"));
//分别具有哪些权限
boolean[] permitted = subject.isPermitted("user:*:*", "product:*:01");
for (boolean b:permitted){
System.out.println("权限:"+b);
}
//同时具有哪些权限
System.out.println(subject.isPermittedAll("user:*:*","product:update:01"));
}
}
}