defaultSecurityManager.setRealm(iniRealm);
//将安全管理器注入安全工具类 用于获取认证的主体
SecurityUtils.setSecurityManager(defaultSecurityManager);
//获取认证的主体
Subject subject = SecurityUtils.getSubject();
//创建令牌
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("mosin", "1234");
try {
//认证 通过没有任何的异常
subject.login(usernamePasswordToken);
//验证是否通过
boolean authenticated = subject.isAuthenticated();
System.out.println("认证通过:"+authenticated);
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户名错误!");
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("密码错误!");
}
}
}
* DisabledAccountException(帐号被禁用)
* LockedAccountException(帐号被锁定)
* ExcessiveAttemptsException(登录失败次数过多)
* ExpiredCredentialsException(凭证过期)等
---
#### 4.5 自定义Realm
上边的程序使用的是Shiro自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm。
###### 1.shiro提供的Realm
![在这里插入图片描述](https://img-blog.csdnimg.cn/a6acaff1809a47cc956b209ff9af4b29.png#pic_center)
###### 2.根据认证源码认证使用的是SimpleAccountRealm
![在这里插入图片描述](https://img-blog.csdnimg.cn/529bfd09ea274db4aace4ebdb6e15297.png#pic_center)
`SimpleAccountRealm的部分源码中有两个方法一个是 认证 一个是 授权`,
public class SimpleAccountRealm extends AuthorizingRealm {
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
SimpleAccount account = getUser(upToken.getUsername());
if (account != null) {
if (account.isLocked()) {
throw new LockedAccountException("Account [" + account + "] is locked.");
}
if (account.isCredentialsExpired()) {
String msg = "The credentials for account [" + account + "] are expired";
throw new ExpiredCredentialsException(msg);
}
}
return account;
}
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = getUsername(principals);
USERS_LOCK.readLock().lock();
try {
return this.users.get(username);
} finally {
USERS_LOCK.readLock().unlock();
}
}
}
###### 3.自定义realm
/**
* 自定义realm
*/
public class CustomerRealm extends AuthorizingRealm {
//认证方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
//授权方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String principal = (String) token.getPrincipal();
if("mosin".equals(principal)){
return new SimpleAuthenticationInfo(principal,"123",this.getName());
}
return null;
}
}
###### 4.使用自定义Realm认证
public class TestAuthenticatorCustomerRealm {
public static void main(String[] args) {
//创建securityManager
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//IniRealm realm = new IniRealm(“classpath:realm.ini”);
//设置为自定义realm获取认证数据
defaultSecurityManager.setRealm(new CustomerRealm());
//将安装工具类中设置默认安全管理器
SecurityUtils.setSecurityManager(defaultSecurityManager);
//获取主体对象
Subject subject = SecurityUtils.getSubject();
//创建token令牌
UsernamePasswordToken token = new UsernamePasswordToken(“mosin”, “1234”);
try {
subject.login(token);//用户登录
System.out.println(“登录成功”);
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println(“用户名错误!!”);
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println(“密码错误!!!”);
}
}
}
#### 4.6 使用MD5和Salt
>
> 实际应用是将盐和散列后的值存在数据库中,自动realm从数据库取出盐和加密后的值由shiro完成密码校验。
>
>
>
###### 1.自定义md5+salt的realm
/**
* 自定义md5+salt realm
*/
public class CustomerMD5Realm extends AuthorizingRealm {
//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
//认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String principal = (String) token.getPrincipal();
//根据用户名查询数据库
if("mosin".equals(principal)){
// 参数1:用户名 参数2:密码 参数3:盐 参数4:自定义realm的名字
System.out.println(this.getName());
return new SimpleAuthenticationInfo(principal, "800d63a19662b2ba95bc2ffa01ab4804", ByteSource.Util.bytes("mosin"),this.getName());
}
return null;
}
}
###### 2.使用md5 + salt 认证
public class CustomerMD5RealmTest {
public static void main(String[] args) {
//创建安全管理器
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//创建自定义MD5Realm对象
CustomerMD5Realm customerMD5Realm = new CustomerMD5Realm();
//创建密码认证匹配器对象
HashedCredentialsMatcher md5 = new HashedCredentialsMatcher("MD5");
//设置散列的次数
md5.setHashIterations(1024);
//设置密码认证匹配器对象
customerMD5Realm.setCredentialsMatcher(md5);
//设置安全管理器的 认证安全数据源
defaultSecurityManager.setRealm(customerMD5Realm);
//设置安全工具类的安全管理器
SecurityUtils.setSecurityManager(defaultSecurityManager);
//获取认证的主体
Subject subject = SecurityUtils.getSubject();
//创建令牌
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("mosi", "12345");
//登录认证
try {
subject.login(usernamePasswordToken);
System.out.println("认证通过:"+subject.isAuthenticated());
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户名错误");
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("密码错误!!!");
}
}
}
---
### 5. shiro中的授权
#### 5.1 授权
授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的。
#### 5.2 关键对象
**授权可简单理解为who对what(which)进行How操作:**
`Who,即主体(Subject)`,主体需要访问系统中的资源。
`What,即资源(Resource)`,如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括`资源类型`和`资源实例`,比如`商品信息为资源类型`,类型为t01的商品为`资源实例`,编号为001的商品信息也属于资源实例。
`How,权限/许可(Permission)`,规定了主体对资源的操作许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为001用户的修改权限等,通过权限可知主体对哪些资源都有哪些操作许可。
#### 5.3 授权流程
![在这里插入图片描述](https://img-blog.csdnimg.cn/05f82d5f28764cee9e9a1e55503e9b8f.png#pic_center)
#### 5.4 授权方式
* **基于角色的访问控制**
+ RBAC基于角色的访问控制(Role-Based Access Control)是以角色为中心进行访问控制
```
if(subject.hasRole("admin")){
//操作什么资源
}
```
* **基于资源的访问控制**
+ RBAC基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问控制
```
if(subject.isPermission("user:update:01")){ //资源实例
//对01用户进行修改
}
if(subject.isPermission("user:update:\*")){ //资源类型
//对01用户进行修改
}
```
#### 5.5 权限字符串
权限字符串的规则是:**资源标识符:操作:资源实例标识符**,意思是对哪个资源的哪个实例具有什么操作,“:”是资源/操作/实例的分割符,权限字符串也可以使用\*通配符。
例子:
* 用户创建权限:user:create,或user:create:\*
* 用户修改实例001的权限:user:update:001
* 用户实例001的所有权限:user:\*:001
#### 5.6 shiro中授权编程实现方式
* **编程式**
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}
* **注解式**
@RequiresRoles(“admin”)
public void hello() {
//有权限
}
* **标签式**
JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成:
<shiro:hasRole name=“admin”>
<!— 有权限—>
</shiro:hasRole>
注意: Thymeleaf 中使用shiro需要额外集成!
*
#### 5.7 开发授权
###### 1.realm的实现
public class CustomerRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String primaryPrincipal = (String) principals.getPrimaryPrincipal();
System.out.println("primaryPrincipal = " + primaryPrincipal);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRole("admin");
simpleAuthorizationInfo.addStringPermission("user:update:\*");
simpleAuthorizationInfo.addStringPermission("product:\*:\*");
return simpleAuthorizationInfo;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String principal = (String) token.getPrincipal();
if("xiaochen".equals(principal)){
String password = "3c88b338102c1a343bcb88cd3878758e";
String salt = "Q4F%";
return new SimpleAuthenticationInfo(principal,password,
ByteSource.Util.bytes(salt),this.getName());
}
return null;
}
}
###### 2.授权
public class CustomerMD5RealmTest {
public static void main(String[] args) {
//创建安全管理器
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//创建自定义MD5Realm对象
CustomerMD5Realm customerMD5Realm = new CustomerMD5Realm();
//创建密码认证匹配器对象
HashedCredentialsMatcher md5 = new HashedCredentialsMatcher("md5");
//设置加密的次数
md5.setHashIterations(1024);
//设置密码认证匹配器对象
customerMD5Realm.setCredentialsMatcher(md5);
//设置安全管理器的 认证安全数据源
defaultSecurityManager.setRealm(customerMD5Realm);
//设置安全工具类的安全管理器
SecurityUtils.setSecurityManager(defaultSecurityManager);
//获取认证的主体
Subject subject = SecurityUtils.getSubject();
//创建令牌
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("mosin", "12345");
//登录认证
try {
subject.login(usernamePasswordToken);
System.out.println("认证通过:"+subject.isAuthenticated());
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户名错误");
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("密码错误!!!");
}
//基于角色的控制
//单角色控制
System.out.println("========hasRole==========");
boolean admin = subject.hasRole("admin");
System.out.println("hash admin role:"+admin);
//多角色控制
System.out.println("========hasAllRoles==========");
List<String> roles = Arrays.asList("admin", "user");
boolean booleans = subject.hasAllRoles(roles);
System.out.println("booleans = " + booleans);
// 基于任意角色的控制
System.out.println("========hasRoles==========");
boolean[] booleans1 = subject.hasRoles(roles);
for (boolean b : booleans1) {
System.out.println("b = " + b);
}
//基于权限字符串的权限控制
System.out.println("========isPermitted==========");
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
s1) {
System.out.println("b = " + b);
}
//基于权限字符串的权限控制
System.out.println("========isPermitted==========");
[外链图片转存中…(img-6aRwlpS4-1714280377218)]
[外链图片转存中…(img-Xszy3qrd-1714280377218)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!