Shiro初步使用
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
这里主要以shiro的入门实战来了解到使用shiro的流程
1、认证
1、使用ini文件的方式使用shiro
INI配置文件
INI配置文件是一种key/value的键值对配置,分为[main]、[users]、[roles]、[urls]四个部分,每一个部分中的key不可重复,#号代表注释,shiro.ini文件默认在/WEB-INF/ 或classpath下,shiro会自动查找,INI配置文件相当于一个静态数据库
。因此无需进行数据库操作
1、创建一个普通maven项目,并导入shiro的相关依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.3</version>
</dependency>
2、在resource目录下创建shiro.ini文件,第一行不可以写错
[users]
zhangsan=123
lisi=123456
3、编写代码
主要步骤有六步:
1.创建安全管理器对象
DefaultSecurityManager securityManager=new DefaultSecurityManager();
2.给安全管理器设置realm(这里使用ini文件)
securityManager.setRealm(new IniRealm(“classpath:shiro.ini”));
3.SecurityUtils 给安全工具类设置全局安全管理器
SecurityUtils.setSecurityManager(securityManager);
4.关键对象subject 主体
Subject subject = SecurityUtils.getSubject();
5.创建令牌
UsernamePasswordToken token=new UsernamePasswordToken(“zhangsan”,“123”);
6.使用token用户认证
subject.login(token);
认证的时候如果用户名不存在会报UnknownAccountException
认证的时候如果密码错误会报IncorrectCredentialsException
public class TestAuthenticator {
public static void main(String[] args) {
//1.创建安全管理器对象
DefaultSecurityManager securityManager=new DefaultSecurityManager();
//2.给安全管理器设置realm
securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
//3.SecurityUtils 给安全工具类设置全局安全管理器
SecurityUtils.setSecurityManager(securityManager);
//4.关键对象subject 主体
Subject subject = SecurityUtils.getSubject();
//5.创建令牌
UsernamePasswordToken token=new UsernamePasswordToken("zhangsan","123");
//6.使用token用户认证
try {
System.out.println("认证状态:"+ subject.isAuthenticated());
subject.login(token);
System.out.println("认证状态:"+ subject.isAuthenticated());
}catch (UnknownAccountException e){
System.out.println("用户名不存在");
e.printStackTrace();
}
catch (IncorrectCredentialsException e) {
System.out.println("密码错误");
e.printStackTrace();
}
}
}
2、创建和使用自定义Realm
由类的关系图所示,ini方式使用的
IniRealm
类是继承了AuthorizingRealm抽象类
,并且事实上自定义的Reaml就是需要继承AuthorizingRealm抽象类
并且重写doGetAuthorizationInfo(授权)
,doGetAuthenticationInfo(认证)
,除了该处,其他与ini方式基本一致
添加链接描述
1、创建自定义Realm(这里使用的虚拟查库)
//创建自定义Realm
public class CustomerRealm extends AuthorizingRealm {
//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//根据该token拿到用户名
String principal= (String) authenticationToken.getPrincipal();
System.out.println(principal);
//根据身份信息查询相关数据库
if ("zhangsan".equals(principal)) {
//第一个参数:数据库中正确的用户名,第二个参数:数据库中正确的密码,第三个参数:提供当前Realm的名字
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("principal", "123", this.getName());
return authenticationInfo;
}
return null;
}
}
2、实现自定义Realm
public class TestCustomerRealmAuthentication {
public static void main(String[] args) {
//创建安全管理
DefaultSecurityManager securityManager=new DefaultSecurityManager();
//设置Realm
securityManager.setRealm(new CustomerRealm());
//使用安全工具使用安全管理
SecurityUtils.setSecurityManager(securityManager);
//使用安全工具获取subject
Subject subject=SecurityUtils.getSubject();
//创建token
UsernamePasswordToken token=new UsernamePasswordToken("zhangsan","123");
try{
securityManager.login(subject,token);
System.out.println();
}catch (UnknownAccountException e){
System.out.println("用户名不存在");
e.printStackTrace();
}catch (IncorrectCredentialsException e){
System.out.println("密码错误");
}
}
}
3、使用md5+盐+多次散列
1、创建一个测试
public class TestShiroMD5 {
public static void main(String[] args) {
//创建一个MD5算法
Md5Hash md5Hash=new Md5Hash("123");
System.out.println(md5Hash.toHex());
//使用MD5+salt
Md5Hash md5Hash1=new Md5Hash("123","xo*56");
System.out.println(md5Hash1.toHex());
//使用MD5+salt+多次散列(默认是散列一次)
Md5Hash md5Hash2=new Md5Hash("123","xo*56",1024);
System.out.println(md5Hash2.toHex());
}
}
输出结果:
202cb962ac59075b964b07152d234b70
da6e378e550da9efcc84976d0a8bc68b
825feb4279a620fd9bf50fa5850fa97c
2、创建Realm类CustomerMd5Realm继承AuthorizingRealm
使用md5
: credentialsMatcher.setHashAlgorithmName(“md5”);测试类中改变默认的credentialsMatcher
加盐
: 在自定义返回的SimpleAuthenticationInfo类的构造方法加入ByteSource.Util.bytes("xo*56")
散列多次
:credentialsMatcher.setHashIterations(1024);
public class CustomerMd5Realm extends AuthorizingRealm {
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取身份信息
String principal= (String) authenticationToken.getPrincipal();
//根据用户名查询数据库
if(principal.equals("zhangsan")){
return new SimpleAuthenticationInfo(principal,
"825feb4279a620fd9bf50fa5850fa97c",
ByteSource.Util.bytes("xo*56") //加盐
,this.getName());
}
return null;
}
}
3、测试(这里只放入(MD5+salt+散列)
//测试Md5
public class TestCustomerMd5RealmAuthentication {
public static void main(String[] args) {
//创建安全管理器
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//设置Realm
CustomerMd5Realm realm = new CustomerMd5Realm();
securityManager.setRealm(realm);
//设置Realm使用Hash凭证匹配器(默认是使用简单的credentialsMatcher)
HashedCredentialsMatcher credentialsMatcher=new HashedCredentialsMatcher();
//md5
credentialsMatcher.setHashAlgorithmName("md5");
//散列
credentialsMatcher.setHashIterations(1024);
realm.setCredentialsMatcher(credentialsMatcher);
//将安全管理器注入安全工具
SecurityUtils.setSecurityManager(securityManager);
//通过安全工具类获取subject
Subject subject = SecurityUtils.getSubject();
//认证
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
try {
subject.login(token);
System.out.println("登录成功");
} catch (UnknownAccountException e) {
System.out.println("该用户名不存在");
}catch (IncorrectCredentialsException e){
System.out.println("密码错误");
}
}
}
2、授权
授权可简单理解为who对what(which)进行How操作:
-
Who,即
主体(Subject)
,主体需要访问系统中的资源。 -
What,
即资源(Resource)
,如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型
和资源实例
,比如商品信息为资源类型,类型为t01的商品为资源实例,编号为001的商品信息也属于资源实例。 -
How,
权限/许可(Permission)
,规定了主体对资源的操作许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为001用户的修改权限等,通过权限可知主体对哪些资源都有哪些操作许可。
这里直接上代码(接着上一部分的认证)
1、在测试类查看用户是否具有某些个角色或者资源的权限
//如果认证通过,进行授权控制
if(subject.isAuthenticated()){
//1.基于角色权限控制
System.out.println(subject.hasRole("admin"));
//2.基于多角色权限控制,调用了两次doGetAuthorizationInfo
System.out.println(subject.hasAllRoles(Arrays.asList("admin","user")));
//3.是否具有其中一个角色
boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "user", "super"));
System.out.println(Arrays.toString(booleans));
//4.基于权限字符串的访问控制,资源标识符:操作:资源类型
System.out.println("=================");
System.out.println("权限"+subject.isPermitted("user:*:01"));
System.out.println("权限"+subject.isPermitted("product:create:01"));
//5.同时具有哪些权限
System.out.println(subject.isPermittedAll("user:*:01","product:create:01"));
//6.分别具有哪些权限
boolean[] permitted = subject.isPermitted("user:*:01", "product:create:01");
System.out.println(Arrays.toString(permitted));
}
2、Realm类的doGetAuthorizationInfo
方法分配权限
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//拿到用户身份信息(用户名)
String principal= (String) principalCollection.getPrimaryPrincipal();
System.out.println("身份信息"+principal);
//给用户添加角色权限
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.addRole("admin");
authorizationInfo.addRole("user");
//将数据库中查询权限信息赋值权限对象
//表示将user模块的所有权限
authorizationInfo.addStringPermission("user:*:*");
//表示product模块的01对象进行create的权限
authorizationInfo.addStringPermission("product:create:01");
return authorizationInfo;
}
mpleAuthorizationInfo();
authorizationInfo.addRole("admin");
authorizationInfo.addRole("user");
//将数据库中查询权限信息赋值权限对象
//表示将user模块的所有权限
authorizationInfo.addStringPermission("user:*:*");
//表示product模块的01对象进行create的权限
authorizationInfo.addStringPermission("product:create:01");
return authorizationInfo;
}
本文代码主要仿照b站编程不良人的shiro教程
链接地址:https://www.bilibili.com/video/BV1uz4y197Zm/?p=12