shiro学习(通俗易懂)
Posted 小样5411
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shiro学习(通俗易懂)相关的知识,希望对你有一定的参考价值。
目录
一、什么是shiro?
shiro是aoache旗下的一个开源框架,它将软件系统的安全认证的功能抽取出来,简单易用,实现用户身份认证,权限授权,加密、会话、管理等功能,组成了一个通用的安全认证框架。
详细了解shiro链接如下:
https://shiro.apache.org/architecture.html
如下图,红色框住的就是shiro最核心部分,图大家简单有个印象即可
若文章有错误地方,欢迎评论交流
二、第一个shiro用户认证程序
认证:我们现在应该知道了,身份认证,就是判断一个用户是否为合法用户的过程,也就是最常用的简单身份认证方式,比如登录时对用户输入的账号密码进行验证,判断是否与系统中存储的用户名和密码一致。
注:我IDEA写的代码会压缩成压缩包放在最后以百度网盘链接给出,供大家参考
shiro中对于不同对象有自己的称呼
1、Subject:主体
如登录的用户
2、Principal:身份信息
如用户的用户名,手机号,邮箱这种具有唯一标识的身份信息,主身份(Primary Principal)作为登录的信息,因为不可能所有身份都作为登录的,那多且太麻烦了,只要账号这种身份信息作为登录即可。
3、Credential:凭证信息
主体对象自己知道的安全信息,如密码。注意下面图,等下认证程序就是跟着这幅图写
构建一个Maven项目,直接构建一个quickstart的即可
建好项目就导入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.3</version>
</dependency>
接下来新建resources文件夹,放入shiro的配置文件,叫shiro.ini,这个配置文件只在刚学习的时候用,后面整合SpringBoot不用这个,这里相当于伪造数据。
[users]
xiaoming=123
zhangsan=123456
yx5411=12345
接着建立一个认证类TestAuthenticator,目录结构如下
import org.apache.shiro.SecurityUtils;
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、给安全管理器设置realm,new IniRealm()是读取shiro.ini
securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
//3、给全局安全工具类SecurityUtils设置安全管理器
SecurityUtils.setSecurityManager(securityManager);
//4、关键对象subject主体
Subject subject = SecurityUtils.getSubject();
//5、创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("xiaoming","123");
try{
System.out.println("认证状态:"+subject.isAuthenticated());
subject.login(token);//用户认证(登录)
System.out.println("认证状态:"+subject.isAuthenticated());
}catch (Exception e){
e.printStackTrace();//认证不通过会报异常
}
}
}
若将令牌的账号变成xiaobai,没有这个账号,则会报未知账户异常
如果是密码不对,就会报凭证不正确异常,也就是密码错误
于是可以专门捕获这两个异常,用于提示用户
三、自定义Realm实现
上面是用配置文件自己伪造了数据,如果我们要连接上数据库呢?这里就需要自定义Realm了,Realm就是和数据库连接的。
上面代码就是运行TestCustomerRealmAuthenticator类,假设登录输入账号密码是xiaoming,1234,由SimpleAuthenticationInfo查到数据库中对应用户名的密码是123,密码不同,就会返回凭证不正确异常(即密码错误),注意这里都是自己弄的假数据,真正连接数据库要用jdbc和mybatis。我们后面都先用CustomerRealm这种造伪数据,也就是假设从数据库中查出的,等真正整合SpringBoot再连接真正数据库。
如果改成下图,数据库中没有输入的用户,就会显示用户名错误
CustomerRealm.java
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;
/**
* 自定义realm实现,将认证/授权数据的来源转为数据库的实现
*/
public class CustomerRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//token中获取用户名
String principal = (String) authenticationToken.getPrincipal();//用户名(输入的)
//根据这个身份信息,用jdbc或者mybatis和数据库中数据比对,看有没有这个用户
//下面我们就不查了,做一个伪数据,假设数据库中只有小明这一个用户数据,后续和SpringBoot整合再连接数据库,假设xiaoming是数据库中仅存的数据
if ("xiaoming".equals(principal)){
//三个参数表示从数据库中查到的账号和密码,最后一个就是Realm的名字,直接用getName即可
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal,"123",this.getName());
return simpleAuthenticationInfo;
}
return null;
}
}
TestCustomerRealmAuthenticator.java
import com.yx.realm.CustomerRealm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
/**
* 使用自定义的realm
*/
public class TestCustomerRealmAuthenticator {
public static void main(String[] args) {
//1、创建SecurityManager
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//2、设置自定义realm
defaultSecurityManager.setRealm(new CustomerRealm());
//3、给全局安全工具类SecurityUtils设置安全管理器
SecurityUtils.setSecurityManager(defaultSecurityManager);
//4、关键对象subject主体
Subject subject = SecurityUtils.getSubject();
//5、创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("xiaoming","1234");
try{
subject.login(token);
}catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("用户名错误");
}catch (IncorrectCredentialsException e){
e.printStackTrace();//认证不通过会报异常
System.out.println("密码错误");
}
}
}
四、MD5算法加密
我们这里是没有进行加密的,伪造查到的数据库中数据123也是明文,不安全,尤其是以后用户支付密码,那就更不能明文存储了
我们会用MD5+Salt进行加密,MD5是不可逆的,只能明文生成密文,同一个明文每次生成的密文都是一致的,有些网站所谓的MD5破解,只是将一些常见的密码,比如123456,root,888888,这种简单密码,做了一个密文数据库,如果有匹配,那就给你返回它存储的明文,但稍微难一点的都不能。所以我们在设置密码的时候,都会提示你要数字+英文字符这样这种网站就推不出来了。但是虽然网站这样提示要求,最后用户也可能输入一个aaa123456这种简单又符合输入要求的,所以就需要加密算法再提升一下,也就是用Salt,让加密更加复杂化,会在MD5加密后的字符串后面再加几个随机复杂字符。
测试一下Md5加密
import org.apache.shiro.crypto.hash.Md5Hash;
public class TestShiroMd5 {
public static void main(String[] args) {
Md5Hash md5Hash = new Md5Hash("123");//给123加密
System.out.println(md5Hash.toHex());
//Md5+salt处理
Md5Hash md5Hash1 = new Md5Hash("123","x0*7p");//这个盐这里定死了,后面一个写一个随机生成字符的方法
System.out.println(md5Hash1.toHex());
//Md5+salt处理+hash散列
Md5Hash md5Hash2 = new Md5Hash("123","x0*7p",1024);//1024表示散列1024次,更加复杂与安全
System.out.println(md5Hash2.toHex());
}
}
加密后就是16进制的32位字符串,最后一个最复杂,也最安全
两个类
TestCustomerMD5RealmAuthenticator .java
import com.yx.realm.CustomerMd5Realm;
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;
public class TestCustomerMD5RealmAuthenticator {
public static void main(String[] args) {
//1、创建SecurityManager
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//2、设置自定义realm
CustomerMd5Realm realm = new CustomerMd5Realm();
//设置realm使用hash凭证匹配器,用md5
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");
realm.setCredentialsMatcher(hashedCredentialsMatcher);
defaultSecurityManager.setRealm(realm);
//3、给全局安全工具类SecurityUtils设置安全管理器
SecurityUtils.setSecurityManager(defaultSecurityManager);
//4、关键对象subject主体
Subject subject = SecurityUtils.getSubject();
//5、创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("xiaoming","123");
try{
subject.login(token);
System.out.println("登录成功");
}catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("用户名错误");
}catch (IncorrectCredentialsException e){
e.printStackTrace();//认证不通过会报异常
System.out.println("密码错误");
}
}
}
CustomerMd5Realm.java
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;
//Md5+salt+hash
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 ("xiaoming".equals(principal)){
//三个参数表示从数据库中查到的账号和密码,最后一个就是Realm的名字,直接用getName即可
return new SimpleAuthenticationInfo(principal,"202cb962ac59075b964b07152d234b70",this.getName());
}
return null;
}
}
用MD5加密需要在设置自定义realm改一改,加一个设置md5,因为默认不是md5,相当于告诉程序用md5加密,运行TestCustomerMD5RealmAuthenticator 类
CustomerMd5Realm.java加入salt
同样运行TestCustomerMD5RealmAuthenticator 类
这里他会自动加上盐,再与数据库中这个加密的字符串比较
再加上Hash散列呢,散列可是散列1024次
TestCustomerMD5RealmAuthenticator 类中加上散列多少次,相当于告诉密码散列了多少次
以上就是MD5+Salt+Hash散列加密方式,我们再把核心代码贴出来
public class TestCustomerMD5RealmAuthenticator {
public static void main(String[] args) {
//1、创建SecurityManager
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//2、设置自定义realm
CustomerMd5Realm realm = new CustomerMd5Realm();
//设置realm使用hash凭证匹配器,用md5
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");
hashedCredentialsMatcher.setHashIterations(1024);
realm.setCredentialsMatcher(hashedCredentialsMatcher);
defaultSecurityManager.setRealm(realm);
//3、给全局安全工具类SecurityUtils设置安全管理器
SecurityUtils.setSecurityManager(defaultSecurityManager);
//4、关键对象subject主体
Subject subject = SecurityUtils.getSubject();
//5、创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("xiaoming","123");
try{
subject.login(token);
System.out.println("登录成功");
}catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("用户名错误");
}catch (IncorrectCredentialsException e){
e.printStackTrace();//认证不通过会报异常
System.out.println("密码错误");
}
}
}
//MD5+Salt+Hash
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 ("xiaoming".equals(principal)){
//三个参数表示从数据库中查到的账号和密码,最后一个就是Realm的名字,直接用getName即可
return new SimpleAuthenticationInfo(principal,
"4d41fdb34ea9f4b5dd2b890a3b89943e",
ByteSource.Util.bytes("x0*7p"),
this.getName());
}
return null;
}
}
五、shiro中的授权
之前是认证,也就是登录进行认证,现在是授权,也就是访问权限,不同用户具有不同的访问权限,如对某些用户密码修改权限,只能管理员才能有。授权是基于认证的,因为你只有合法登录通过,你才有一些访问权限。
两种授权方式:1、基于角色的访问控制 2、基于资源的访问控制
我们直接来实践一下,再来讲解,我们接着用md5+salt
+hash的认证后面加内容,因为授权要先认证通过,也就是要先登录通过。
CustomerMd5Realm.java
首先基于角色的访问控制,假设xiaoming有两个角色,admin和user,后面SpringBoot整合真正写数据库,现在先造这两个数据,一般用户有哪些角色都会存在数据库中。
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.以上是关于shiro学习(通俗易懂)的主要内容,如果未能解决你的问题,请参考以下文章