Shiro基本功能
Shiro架构
- 从外部来看shiro的架构
Application Code:应用代码
Subject:主体(用户,第三方等与程序对接的会话主体)
Security Manager 安全核心(Shrio 的核心功能)
realm:域,相当于数据源,通过realm存取认证、授权相关数据。(securityManager的授权和认证的逻辑最终都是交给在这里执行的,可以理解为调用数据库查询一系列的信息,然后用这些信息和login的参数进行匹配,一般情况下在项目中采用自定义的realm,因为不同的业务需求不一样)
- 内部框架
环境搭建
导入配置
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
Shiro获取权限相关信息可以通过数据库获取,也可以通过ini配置文件获取
登录认证
首先说明登录认证的基本流程是
- 收集用户名和密码也就是用户的凭证
- 调用
Subject.login()
进行登录 - 创建自定义的Realm类,继承
AuthorizingRealm
类,实现doGetAuthenticationInfo()
示例:
package com.sftest.logintest;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
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 logintest {
public static void main(String[] args) {
//1. 初始化获取SecurityManager
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
//2. 获取Subject对象
Subject subject = SecurityUtils.getSubject();
//3. 创建token对象,web应用用户名密码从页面传递
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "z3");
//4. 完成登录
try {
subject.login(token);
System.out.println("登录成功");
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户不存在");
} catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码错误");
} catch (AuthenticationException e) {
e.printStackTrace();
}
}
}
问题解决:
F:Ctrl+Alt+V快捷键突然使用不了?
Q:查看是否是快捷键冲突占用,QQ音乐的快捷键冲突,关闭即可
F:报错java: JDK isn’t specified for module
Q:IDEA自带的JDK有问题,换成自己安装的JDK就可以了
示例结果
接下来对这段简单的登录验证逻辑进行Debug,查看具体代码实现。
在login
处下个断点,调试走到这里,可以看到实际上调用的login
方法是在DelegatingSubject
类中的login
方法
到这里看到username和password的值,然后看到实际上是securitymanager调用login方法
继续跟进,DefaultSecurityManager
下的login
方法,已经走到了authenticate()
走到AuthenticatingSecurityManager
类下的authenticate
方法,一个认证器(authenticator)调用了authenticate
方法
还没看到方法实现的地方,继续跟进,最终在AbstractAuthenticator
类中找到了token,发现又交给了另一个方法去处理了
真能藏,然后到这里它改名字了,叫authenticationToken
,然后这里有个逻辑,同步也是发现这里用到了realm对象,调试到这一步的时候就已经发现size=1,所以这里调用的应该是doSingleRealmAuthentication
方法
继续跟进,到了doSingleRealmAuthentication
类中,又发现token传递到了一个方法里面
继续跟,到这里终于看到了有点逻辑了,看下它又做了什么
好像是获取这个token相关的缓存了,但是没有获取到,所以info为null,然后就继续看,发现处理token的方法是doGetAuthenticationInfo
而 Shiro的认证依赖AuthenticatingRealm
里的getAuthenticationInfo
方法,该方法会调用我们自定义的认证方法<font style="color:rgb(77, 77, 77);">doGetAuthenticationInfo</font>
,获取本次认证的结果。
最终走完过程(没有走rememberme这个逻辑)
角色授权
授权:访问控制(访问页面,编辑数据,页面操作),在授权中用到的几个对象:Subject(主体),Resource(资源),Permission(权限),Role(角色)
- 角色授权的三种方式
- 代码式:通过
if{}else{}
来进行控制 - 注解:使用注解,有权限则进一步调用,没权限抛出异常
@RequiresRoles
- 标签:Shiro自带标签
<shiro:hasRole name="admin"></shiro:hasRole>
设置角色需要在shiro.ini中进行配置
判断用户是否拥有角色权限:
//shiro.ini
[users]
zhangsan=z3, role1, role2
//.java
boolean role1 = subject.hasRole("role1");
用户操作权限,在配置文件中进行设置
//shiro.ini
[roles]
role1=user:insert, user:select
//.java
boolean permitted = subject.isPermitted("user:insert");
还有一个方法来检查权限
subject.checkPermission("user:select");
没有返回值,有权限直接向下执行,没有权限抛出异常。
Shiro加密
- Md5加密测试
为保障安全,还可以进行md5多次迭代加密。
package com.sftest.logintest;
import org.apache.shiro.crypto.hash.Md5Hash;
public class CryptoTest {
public static void main(String[] args) {
//MD5加密测试
String password = "m0rewuwuwuwu";
Md5Hash md5Hash = new Md5Hash(password);
System.out.println(md5Hash.toHex());
//加盐
Md5Hash woc = new Md5Hash(password, "woc");
System.out.println(woc.toHex());
//多次迭代加盐
Md5Hash hash = new Md5Hash(password, "woc", 3);
System.out.println(hash);
//使用父类进行加密
SimpleHash simpleHash = new SimpleHash("MD5","m0rewuwuwuwu","woc",3);
System.out.println(simpleHash);
}
}
然后看了下MD5加密的源码,其实是
观察使用父类进行加密的代码,似乎明白了点什么。