流程
Shiro的执行流程:
Subject --> SecurityManager --> Realm
Subject:主体,相当于当前用户,所有用户将由SecurityManager来管理
SecurityManager:安全管理器,它管理着所有 Subject,它是 Shiro 的核心
Realm:域,Shiro 从从 Realm 获取安全数据,SecurityManager需要从Realm中获取数据进行校验
所需依赖
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.7.1</version>
</dependency>
Shiro 身份验证
在resource目录创建shiro.ini文件
[users]
zhang=123
wang=123
[User]下的键值对形式的数据,左边为帐号,右边为密码。
Test类:验证帐号密码是否正确
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
public class Test1 {
public static void main(String[] args) {
//1、加载配置文件得到安全管理工厂类对象
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2、获取安全管理类对象
SecurityManager securityManager = factory.getInstance();
//3、绑定使用的管理器
SecurityUtils.setSecurityManager(securityManager);
//4、得到Subject主体用户对象
Subject subject = SecurityUtils.getSubject();
//5、及创建用户名/密码身份验证Token(即用户身份/凭证)
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
try {
//6、用户登录,如果不发生异常,用户登录成功
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
/**
* 如果身份验证失败请捕获 AuthenticationException 或其子类,常见的如:
* DisabledAccountException(禁用的帐号)
* LockedAccountException(锁定的帐号)、
* UnknownAccountException(错误的帐号)、
* ExcessiveAttemptsException(登录失败次数过多)、
* IncorrectCredentialsException (错误的凭证)、
* ExpiredCredentialsException(过期的凭证)等,具体请查看其继承关系;
* 对于页面的错误消息展示,最好使用如 “用户名 / 密码错误” 而不是 “用户名错误”/“密码错误”,
* 防止一些恶意用户非法扫描帐号库;
*/
}
//7、判断用户登录状态
boolean authenticated = subject.isAuthenticated();
if (authenticated) {
System.out.println("用户登录成功");
}else {
System.out.println("用户登录失败");
}
subject.logout();
}
}
执行 subject.login(token)后,shiro根据shiro.ini文件数据进行比对。
注意创建安全管理类对象,这是shiro的核心,我们验证用户登录让主题把用login()方法,其他事情shiro会帮我们搞定。
授权(文件方式)
在resource目录创建shiro.ini文件
[users]
zhang=123,role1,role2
wang=123456
[roles]
role1=user:create,user:update
role2=user:create,user:delete
分配角色,在密码的后用逗号隔开写上拥有的角色
[roles]代表角色,role1代表拥有user角色的create权限和update权限。
Test类:验证权限
import com.entor.realm.RoleRealm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
public class Test2 {
public static void main(String[] args) {
//创建安全管理器对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//设置realm对象
securityManager.setRealm(new RoleRealm());
//绑定安全管理器
SecurityUtils.setSecurityManager(securityManager);
//获取用户主体对象
Subject subject = SecurityUtils.getSubject();
//创建用户名/密码身份验证Token(即用户身份/凭证)
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
try {
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
}
//7、判断用户登录状态
boolean authenticated = subject.isAuthenticated();
if (authenticated) {
System.out.println("用户登录成功");
} else {
System.out.println("用户登录失败");
}
boolean role1 = subject.hasRole("role1");
if (role1) {
System.out.println("有role1角色");
} else {
System.out.println("没有role1角色");
}
boolean role2 = subject.hasRole("role2");
if (role2) {
System.out.println("有role2角色");
} else {
System.out.println("没有role2角色");
}
//判断用户是否拥有权限
boolean permitted = subject.isPermitted("user:create");
if (permitted) {
System.out.println("拥有权限user:create");
} else {
System.out.println("没有权限user:create");
}
permitted = subject.isPermitted("user:delete");
if (permitted) {
System.out.println("拥有权限user:delete");
} else {
System.out.println("没有权限user:delete");
}
subject.logout();
}
}
自定义Realm(用自定义的Realm来进行认证)
在resource目录创建shiro.ini文件
;声明一个Realm
testRealm=com.entor.realm.TestRealm
;指定securityManager的Realm
securityManager.realms=$testRealm
自定义Realm
import org.apache.shiro.authc.*;
import org.apache.shiro.realm.Realm;
/**
* 自定义Realm,处理认证逻辑
*/
public class TestRealm implements Realm {
public String getName() {
return "TestRealm";
}
/**
* 该Realm支持的Token类型,返回false表示不支持,true表示支持,支持才会执行对应的认证的方法
*
* @param token
* @return
*/
public boolean supports(AuthenticationToken token) {
//仅支持UsernamePasswordToken类型的Token
return token instanceof UsernamePasswordToken;
}
/**
* 认证方法
*/
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken token1 = (UsernamePasswordToken) token;
String username = token1.getUsername();
String password = new String(token1.getPassword());
if (!username.equals("zhang")) {
throw new UnknownAccountException("未知用户");
}
if (!password.equals("123")) {
throw new IncorrectCredentialsException("密码错误");
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password, getName());
return info;
}
}
SecurityManager最终调用Realm来处理我们的认证功能,我们可以注入自己的Reaml。
Test1类
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
public class Test1 {
public static void main(String[] args) {
//1、加载配置文件得到安全管理工厂类对象
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2、获取安全管理类对象
SecurityManager securityManager = factory.getInstance();
//3、绑定使用的管理器
SecurityUtils.setSecurityManager(securityManager);
//4、得到Subject主体用户对象
Subject subject = SecurityUtils.getSubject();
//5、及创建用户名/密码身份验证Token(即用户身份/凭证)
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
try {
//6、用户登录,如果不发生异常,用户登录成功
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
/**
* 如果身份验证失败请捕获 AuthenticationException 或其子类,常见的如:
* DisabledAccountException(禁用的帐号)
* LockedAccountException(锁定的帐号)、
* UnknownAccountException(错误的帐号)、
* ExcessiveAttemptsException(登录失败次数过多)、
* IncorrectCredentialsException (错误的凭证)、
* ExpiredCredentialsException(过期的凭证)等,具体请查看其继承关系;
* 对于页面的错误消息展示,最好使用如 “用户名 / 密码错误” 而不是 “用户名错误”/“密码错误”,
* 防止一些恶意用户非法扫描帐号库;
*/
}
//7、判断用户登录状态
boolean authenticated = subject.isAuthenticated();
if (authenticated) {
System.out.println("用户登录成功");
}else {
System.out.println("用户登录失败");
}
subject.logout();
}
}
自定义AuthorizingRealm来授权
在resource目录创建shiro.ini文件
[main]
;声明一个Realm
roleRealm=com.entor.realm.RoleRealm
;指定securityManager的Realm
securityManager.realm=$roleRealm
自定义AuthorizingRealm:先认证后授权
import org.apache.shiro.authc.*;
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;
import java.util.ArrayList;
public class RoleRealm extends AuthorizingRealm {
/**
* 获取用户授权信息
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取当前登录认证的用户名
String name = (String) principalCollection.getPrimaryPrincipal();
//真实做法:根据用户名去数据库查询对应的角色和权限到AuthorizationInfo对象中
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//用户拥有的角色列表
ArrayList<String> roles = new ArrayList<String>();
roles.add("role1");
//用户拥有的权限列表
ArrayList<String> permission = new ArrayList<String>();
permission.add("user:create");
permission.add("user:update");
//添加角色
info.addRoles(roles);
//添加授权
info.addStringPermissions(permission);
return info;
}
/**
* 获取用户认证信息
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken token1 = (UsernamePasswordToken) token;
String username = token1.getUsername();
String password = new String(token1.getPassword());
if (!username.equals("zhang")) {
throw new UnknownAccountException("未知用户");
}
if (!password.equals("123")) {
throw new IncorrectCredentialsException("密码错误");
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password, getName());
return info;
}
}
TestRole类
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
public class TestRole {
public static void main(String[] args) {
//1、加载配置文件得到安全管理工厂类对象
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro-role.ini");
//2、获取安全管理类对象
SecurityManager securityManager = factory.getInstance();
//3、绑定使用的管理器
SecurityUtils.setSecurityManager(securityManager);
//4、得到Subject主体用户对象
Subject subject = SecurityUtils.getSubject();
//5、创建用户名/密码身份验证Token(即用户身份/凭证)
String usernmae = "zhang";
String password = "123";
String salt = "123456";
UsernamePasswordToken token = new UsernamePasswordToken(usernmae, password);
try {
//6、用户登录,如果不发生异常,用户登录成功
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
/**
* 如果身份验证失败请捕获 AuthenticationException 或其子类,常见的如:
* DisabledAccountException(禁用的帐号)
* 、LockedAccountException(锁定的帐号)、
* UnknownAccountException(错误的帐号)、
* ExcessiveAttemptsException(登录失败次数过多)、
* IncorrectCredentialsException (错误的凭证)、
* ExpiredCredentialsException(过期的凭证)等,具体请查看其继承关系;
* 对于页面的错误消息展示,最好使用如 “用户名 / 密码错误” 而不是 “用户名错误”/“密码错误”,
* 防止一些恶意用户非法扫描帐号库;
*/
}
//7、判断用户登录状态
boolean authenticated = subject.isAuthenticated();
if (authenticated) {
System.out.println("用户登录成功");
} else {
System.out.println("用户登录失败");
}
boolean role1 = subject.hasRole("role1");
if (role1) {
System.out.println("有role1角色");
} else {
System.out.println("没有role1角色");
}
boolean role2 = subject.hasRole("role2");
if (role2) {
System.out.println("有role2角色");
} else {
System.out.println("没有role2角色");
}
//判断用户是否拥有权限
boolean permitted = subject.isPermitted("user:create");
if (permitted) {
System.out.println("拥有权限user:create");
} else {
System.out.println("没有权限user:create");
}
permitted = subject.isPermitted("user:delete");
if (permitted) {
System.out.println("拥有权限user:delete");
} else {
System.out.println("没有权限user:delete");
}
subject.logout();
}
}
加密方式
/* //jdk自带的加密
String str = "hello";
System.out.println("编码前:"+str);
String encode = java.util.Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8));
System.out.println("编码后:"+encode);
String decode = Base64.decodeToString(encode);
System.out.println("解码后:"+decode);*/
/* //Base64加密
String str = "hello";
System.out.println("编码前:"+str);
String encode = Base64.encodeToString(str.getBytes(StandardCharsets.UTF_8));
System.out.println("编码后:"+encode);
String decode = Base64.decodeToString(encode);
System.out.println("解码后:"+decode);*/
/* //使用16进制的方式工具类Hex类编码解码
String str = "hello";
System.out.println("编码前:"+str);
String encode = Hex.encodeToString(str.getBytes());
System.out.println("编码后:"+encode);
String decode = new String(Hex.decode(encode.getBytes()));
System.out.println("解码后:"+decode);*/
/* //MD5加密
String str = "hello";
String salt = "123";
Md5Hash md5Hash = new Md5Hash(str, salt,1);
String md5 = md5Hash.toString();//还可以转换为 toBase64()/toHex()
System.out.println("md5:"+md5);//86fcb4c0551ea48ede7df5ed9626eee7
System.out.println("base64:"+md5Hash.toBase64());
System.out.println("hex:"+md5Hash.toHex());*/
//Shiro 还提供了通用的散列支持
String str = "hello";
String salt = "123";
//内部使用MessageDigest 常用SHA-1 SHA-256 SHA-512 MD5
String simpleHash = new SimpleHash("SHA-1", str, salt).toString();
System.out.println(simpleHash);
Shiro加密
在resource目录创建shiro.ini文件
[main]
credentialsMatcher=com.entor.matcher.MyHashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=md5
credentialsMatcher.hashIterations=2
credentialsMatcher.storedCredentialsHexEncoded=true
;声明一个Realm
roleRealm=com.entor.realm.RoleRealm
roleRealm.credentialsMatcher=$credentialsMatcher
;指定securityManager的Realm
securityManager.realm=$roleRealm
credentialsMatcher是用来设置加密规则的
加密规则类:MyHashedCredentialsMatcher
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
/**
* 自定义密码校验器
*/
public class MyHashedCredentialsMatcher extends HashedCredentialsMatcher {
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
return super.doCredentialsMatch(token, info);
}
}
自定义AuthorizingRealm 来认证
import org.apache.shiro.authc.*;
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;
import java.util.ArrayList;
public class RoleRealm extends AuthorizingRealm {
/**
* 获取用户授权信息
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取当前登录认证的用户名
String name = (String) principalCollection.getPrimaryPrincipal();
//真实做法:根据用户名去数据库查询对应的角色和权限到AuthorizationInfo对象中
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//用户拥有的角色列表
ArrayList<String> roles = new ArrayList<String>();
roles.add("role1");
//用户拥有的权限列表
ArrayList<String> permission = new ArrayList<String>();
permission.add("user:create");
permission.add("user:update");
//添加角色
info.addRoles(roles);
//添加授权
info.addStringPermissions(permission);
return null;
}
/**
* 获取用户认证信息
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken token1 = (UsernamePasswordToken) token;
String username = token1.getUsername();
String password = "cfb13c831a62c3873ceaa206d7acecc5";
if (!username.equals("zhang")) {
throw new UnknownAccountException("未知用户");
}
// if (!password.equals("cfb13c831a62c3873ceaa206d7acecc5")) {
// throw new IncorrectCredentialsException("密码错误");
// }
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password, getName());
//注入加密的盐,
info.setCredentialsSalt(ByteSource.Util.bytes(username+123456));
return info;
}
}
TestRole类:
public class TestRole {
public static void main(String[] args) {
//1、加载配置文件得到安全管理工厂类对象
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro-role.ini");
//2、获取安全管理类对象
SecurityManager securityManager = factory.getInstance();
//3、绑定使用的管理器
SecurityUtils.setSecurityManager(securityManager);
//4、得到Subject主体用户对象
Subject subject = SecurityUtils.getSubject();
//5、创建用户名/密码身份验证Token(即用户身份/凭证)
String usernmae = "zhang";
String password = "123";
String salt = "123456";
/* //2次md5加密
password = new SimpleHash("MD5", password, usernmae+salt, 2).toString();
System.out.println(password);//cfb13c831a62c3873ceaa206d7acecc5*/
UsernamePasswordToken token = new UsernamePasswordToken(usernmae, password);
try {
//6、用户登录,如果不发生异常,用户登录成功
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
/**
* 如果身份验证失败请捕获 AuthenticationException 或其子类,常见的如:
* DisabledAccountException(禁用的帐号)
* 、LockedAccountException(锁定的帐号)、
* UnknownAccountException(错误的帐号)、
* ExcessiveAttemptsException(登录失败次数过多)、
* IncorrectCredentialsException (错误的凭证)、
* ExpiredCredentialsException(过期的凭证)等,具体请查看其继承关系;
* 对于页面的错误消息展示,最好使用如 “用户名 / 密码错误” 而不是 “用户名错误”/“密码错误”,
* 防止一些恶意用户非法扫描帐号库;
*/
}
//7、判断用户登录状态
boolean authenticated = subject.isAuthenticated();
if (authenticated) {
System.out.println("用户登录成功");
} else {
System.out.println("用户登录失败");
}
boolean role1 = subject.hasRole("role1");
if (role1) {
System.out.println("有role1角色");
} else {
System.out.println("没有role1角色");
}
boolean role2 = subject.hasRole("role2");
if (role2) {
System.out.println("有role2角色");
} else {
System.out.println("没有role2角色");
}
//判断用户是否拥有权限
boolean permitted = subject.isPermitted("user:create");
if (permitted) {
System.out.println("拥有权限user:create");
} else {
System.out.println("没有权限user:create");
}
permitted = subject.isPermitted("user:delete");
if (permitted) {
System.out.println("拥有权限user:delete");
} else {
System.out.println("没有权限user:delete");
}
subject.logout();
}
}
首先在RoleTest中,我们输入的username=“zhang”,password=“123”,其加载到我们自定义的认证器后doGetAuthenticationInfo方法中假设我们从数据库拿到的密码为:cfb13c831a62c3873ceaa206d7acecc5,我们将用户数据info返回前我们设置了我们的加密时用的盐,当doGetAuthenticationInfo()返回后,shiro会从我们的shiro.ini文件中读取到我们的加密规则和我们设置的盐,这就完成一次加密校验。