流程如下:
1、Shiro把用户的数据封装成标识token,token一般封装着用户名,密码等信息
2、使用Subject门面获取到封装着用户的数据的标识token
3、Subject把标识token交给SecurityManager,在SecurityManager安全中心中,SecurityManager
把标识token委托给认证器Authenticator进行身份验证。认证器的作用一般是用来指定如何验证,它规定本次认证用到哪些Realm
4、认证器Authenticator将传入的标识token,与数据源Realm对比,验证token是否合法
1.坐标
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
2.设置shrio-test-01.ini模拟数据库
[users]
jx=123456
djx=654321
hcy=147258
3.test类测试–>通过工厂创建SecurityManager对象,并认证
@Test
public void show1(){
//1.创建工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shrio_test_01.ini");
//2.获取对象
SecurityManager instance = factory.getInstance();
//3.绑定当前对象到运行环境
SecurityUtils.setSecurityManager(instance);
//4.当前环境获取subject
Subject subject = SecurityUtils.getSubject();
//用户
String username= "jx";
String userpasswd= "123456";
//5.获取到令牌
UsernamePasswordToken token = new UsernamePasswordToken(username, userpasswd);
//6.登录验证
subject.login(token);
System.out.println("----------------"+subject.isAuthenticated());
}
1.创建MyRealm,继承AuthorizingRealm,实现两个方法
package com.ape.shrio;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.mgt.RealmSecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import java.util.ArrayList;
/**
* @author jx
* @version 1.0
* @since 2023/11/6
**/
public class MyRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证操作");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
String userpwd = new String(token.getPassword());
if ("jx".equals(username) && "123456".equals(userpwd)){
SimpleAuthenticationInfo myshrio =
new SimpleAuthenticationInfo(username, userpwd, "myRealm");//1.安全数据 2.用户密码 3.当前realm
return myshrio;
}else{
System.out.println("账号密码错误");
throw new RuntimeException("账号密码错误");
}
}
}
2.设置shrio-test-02.ini模拟数据库
[users] # 用户
jx=123456,role1,role2
djx=654321,role2
[roles] # 角色
role1=user:save,user:update,user:delete
role2=user:find
3.test类测试
@Test
public void show(){
//创建工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shrio_test_02.ini");
//获取对象
SecurityManager instance = factory.getInstance();
//绑定当前对象到运行环境
SecurityUtils.setSecurityManager(instance);
//当前环境获取subject
Subject subject = SecurityUtils.getSubject();
//用户
String username = "jx";
String userpwd = "123456";
//获取到令牌
UsernamePasswordToken token = new UsernamePasswordToken(username, userpwd);
//登录验证
subject.login(token);
// 角色,权限
System.out.println(subject.isAuthenticated());
System.out.println(subject.hasRole("role1")); //true
System.out.println(subject.hasRole("role2")); //true
System.out.println(subject.isPermitted("user:save")); //true
System.out.println(subject.isPermitted("user:find")); //true
}
1.设置shrio-test-03.ini声明myRealm
[main]
# 声明realm
myClass=com.ape.shrio.MyRealm
securityManager.realms=$myClass
2.创建MyRealm,继承AuthorizingRealm,实现重写两个方法
package com.ape.shrio;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.mgt.RealmSecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import java.util.ArrayList;
/**
* @author jx
* @version 1.0
* @since 2023/11/6
**/
public class MyRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权方法");
String username = (String)principalCollection.getPrimaryPrincipal();
ArrayList<String> roles = new ArrayList<>();
roles.add("role1");
roles.add("role2");
ArrayList<String> authoriys = new ArrayList<>();
authoriys.add("user:save");
authoriys.add("user:update");
authoriys.add("user:delete");
authoriys.add("user:find");
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.addRoles(roles);
authorizationInfo.addStringPermissions(authoriys);
return authorizationInfo;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证操作");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
String userpwd = new String(token.getPassword());
if ("jx".equals(username) && "123456".equals(userpwd)){
SimpleAuthenticationInfo myshrio =
new SimpleAuthenticationInfo(username, userpwd, "myRealm");//1.安全数据 2.用户密码 3.当前realm
return myshrio;
}else{
System.out.println("账号密码错误");
throw new RuntimeException("账号密码错误");
}
}
}
3.测试
@Test
public void show(){
//创建工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shrio_test_03.ini");
//获取对象
SecurityManager instance = factory.getInstance();
//绑定当前对象到运行环境
SecurityUtils.setSecurityManager(instance);
//当前环境获取subject
Subject subject = SecurityUtils.getSubject();
//用户
String username = "jx";
String userpwd = "123456";
//获取到令牌
UsernamePasswordToken token = new UsernamePasswordToken(username, userpwd);
//登录验证
subject.login(token);
System.out.println(subject.isAuthenticated());
System.out.println(subject.hasRole("role1")); //true
System.out.println(subject.hasRole("role2")); //true
System.out.println(subject.isPermitted("user:save")); //true
System.out.println(subject.isPermitted("user:find")); //true
}
Shiro提供了base64和16进制字符串编码/解码的API支持,方便一些编码解码操作。
Shiro内部的一些数据的【存储/表示】都使用了base64和16进制字符串
散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,常见的散列算法如MD5、SHA等。一般进行散列时最好提供一个salt(盐),比如加密密码“admin”,产生的散列值是“21232f297a57a5a743894a0e4a801fc3”,可以到一些md5解密网站很容易的通过散列值得到密码“admin”,即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,如salt(即盐);这样散列的对象是“密码+salt”,这样生成的散列值相对来说更难破解。
shiro支持的散列算法:
Md2Hash、Md5Hash、Sha1Hash、Sha256Hash、Sha384Hash、Sha512Hash
1.构建编码,解码类
package com.ape.code;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.codec.Hex;
/**
* @author jx
* @version 1.0
* @since 2023/11/6
**/
public class GetCode {
// 编码
public static String Hexencode(byte[] bytes){
return new String(Hex.encode(bytes));
}
// 解码
public static byte[] Hexdecode(String code){
return Hex.decode(code);
}
// 编码
public static String Base64encode(byte[] bytes){
return new String(Base64.encode(bytes));
}
// 解码
public static byte[] Base64decode(String code){
return Base64.decode(code);
}
}
2.测试类
//Hex
@Test
public void show1(){
String code = "123456789";
String hexencode = GetCode.Hexencode(code.getBytes());
System.out.println(hexencode);
byte[] hexdecode = GetCode.Hexdecode(hexencode);
System.out.println(new String(hexdecode));
}
//Base64
@Test
public void show2(){
String code = "apesource";
String hexencode = GetCode.Base64encode(code.getBytes());
System.out.println(hexencode);
byte[] hexdecode = GetCode.Base64decode(hexencode);
System.out.println(new String(hexdecode));
}
4.1授权流程
1、首先调用Subject.isPermitted/hasRole接口,其会委托给SecurityManager。
2、SecurityManager接着会委托给内部组件Authorizer;
3、Authorizer再将其请求委托给我们的Realm去做;Realm才是真正干活的;
4、Realm将用户请求的参数封装成权限对象。再从我们重写的doGetAuthorizationInfo方法中获取从数据库中查询到的权限集合。
5、Realm将用户传入的权限对象,与从数据库中查出来的权限对象,进行一一对比。如果用户传入的权限对象在从数据库中查出来的权限对象中,则返回true,否则返回false。
进行授权操作的前提:用户必须通过认证。
Shiro内置了很多默认的过滤器,比如身份验证、授权等相关的。默认过滤器可以参考org.apache.shiro.web.filter.mgt.DefaultFilter中的枚举过滤器
过滤器 | 过滤器类 | 说明 | 默认 |
---|---|---|---|
authc | FormAuthenticationFilter | 基于表单的过滤器;如“/**=authc”,如果没有登录会跳到相应的登录 | 无 |
logout | LogoutFilter | 退出过滤器,主要属性:redirectUrl:退出成功后重定向的地址,如“/logout=logout” | / |
anon | AnonymousFilter | 匿名过滤器,即不需要登录即可访问;一般用于静态资源过滤;示例“/static/**=anon” | 无 |
user | UserFilter | 用户拦截器,用户已经身份验证/记住我登录的都可;示例“/**=user” | 无 |
authcBasic | BasicHttpAuthenticationFilter | Basic HTTP身份验证拦截器,主要属性: applicationName:弹出登录框显示的信息(application); | 无 |
过滤器 | 过滤器类 | 说明 |
---|---|---|
roles | RolesAuthorizationFilter | 角色授权拦截器,验证用户是否拥有所有角色; 主要属性: loginUrl:登录页面地址(/login.jsp);unauthorizedUrl:未授权后重定向的地址;示例“/admin/**=roles[admin]” |
perms | PermissionsAuthorizationFilter | 权限授权拦截器,验证用户是否拥有所有权限;属性和roles一样;示例“/user/**=perms[“user:create”]” |
port | PortFilter | 端口拦截器,主要属性:port(80):可以通过的端口;示例“/test= port[80]”,如果用户访问该页面是非80,将自动将请求端口改为80并重定向到该80端口,其他路径/参数等都一样 |
rest | HttpMethodPermissionFilter | rest风格拦截器,自动根据请求方法构建权限字符串(GET=read, POST=create,PUT=update,DELETE=delete,HEAD=read,TRACE=read,OPTIONS=read, MKCOL=create)构建权限字符串;示例“/users=rest[user]”,会自动拼出“user:read,user:create,user:update,user:delete”权限字符串进行权限匹配(所有都得匹配,isPermittedAll); |
ssl | SslFilter | SSL拦截器,只有请求协议是https才能通过;否则自动跳转会https端口(443);其他和port拦截器一样; |