全文目录:
-
Shiro的简介
-
Shiro具有的功能特性
-
Shiro的架构
-
Shiro的认证过程
-
Shiro的授权过程
-
自定义Realm
-
Shiro加密及(加盐+多次加密)
-
项目中的使用
Shiro的简介
Apache Shiro 是一个能够完全处理身份验证、授权、加密和会话管理的强大灵活的开源安全框架。
注意: Shiro不会去维护用户、维护权限,这些需要我们自己去设计/提供,然后通过相应的接口注入给Shiro
Shiro具有的功能特性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wCiLJo48-1608227209798)(resources/E72930A8351CCF1590779EA87AC5CB65.jpg)]
- Authentication(认证):用户身份识别,通常被称为用户“登录
- Authorization(授权):访问控制。比如某个用户是否具有某个操作的使用权限。
- Session Management(会话管理):特定于用户的会话管理,甚至在非web 或 EJB 应用程序。
- Cryptography(加密):在对数据源使用加密算法加密的同时,保证易于使用。
- Web支持:Shiro的Web支持API有助于保护Web应用程序。
- 缓存:缓存是Apache Shiro API中的第一级,以确保安全操作保持快速和高效。
- 并发性:Apache Shiro支持具有并发功能的多线程应用程序。
- 测试:存在测试支持,可帮助您编写单元测试和集成测试,并确保代码按预期得到保障。
- “运行方式”:允许用户承担另一个用户的身份(如果允许)的功能,有时在管理方案中很有用。
- “记住我”:记住用户在会话中的身份,所以用户只需要强制登录即可。
Shiro的架构
在概念层,Shiro 架构包含三个主要的理念:Subject,SecurityManager和 Realm。下面的图展示了这些组件如何相互作用,我们将在下面依次对其进行描述。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OjK77yFu-1608227209800)(resources/C0841DFC8CB19A94C322EEF635371CF6.jpg)]
- Subject:当前用户,Subject 可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它–当前和软件交互的任何事件。
- SecurityManager:管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞。
- Realms:用于进行权限信息的验证,我们自己实现。Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)。
我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
自定义 Realm
Shiro 框架内部默认提供了两种实现,一种是查询.ini
文件的IniRealm
,另一种是查询数据库的JdbcRealm
,这两种来说都相对简单,感兴趣的可以去【这里】瞄两眼,我们着重就来介绍介绍自定义实现的 Realm 吧。
先在合适的包下创建一个【MyRealm】类,继承 Shirot 框架的 AuthorizingRealm 类,并实现默认的两个方法:
/**
* 模拟数据库数据
*/
Map<String, String> userMap = new HashMap<>(16);
{
userMap.put("wmyskxz", "123456");
super.setName("myRealm"); // 设置自定义Realm的名称,取什么无所谓..
}
/**
* 授权
*
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String userName = (String) principalCollection.getPrimaryPrincipal();
// 从数据库获取角色和权限数据
Set<String> roles = getRolesByUserName(userName);
Set<String> permissions = getPermissionsByUserName(userName);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setStringPermissions(permissions);
simpleAuthorizationInfo.setRoles(roles);
return simpleAuthorizationInfo;
}
/**
* 模拟从数据库中获取权限数据
*
* @param userName
* @return
*/
private Set<String> getPermissionsByUserName(String userName) {
Set<String> permissions = new HashSet<>();
permissions.add("user:delete");
permissions.add("user:add");
return permissions;
}
/**
* 模拟从数据库中获取角色数据
*
* @param userName
* @return
*/
private Set<String> getRolesByUserName(String userName) {
Set<String> roles = new HashSet<>();
roles.add("admin");
roles.add("user");
return roles;
}
/**
* 认证
*
* @param authenticationToken 主体传过来的认证信息
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 1.从主体传过来的认证信息中,获得用户名
String userName = (String) authenticationToken.getPrincipal();
// 2.通过用户名到数据库中获取凭证
String password = getPasswordByUserName(userName);
if (password == null) {
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("wmyskxz", password, "myRealm");
return authenticationInfo;
}
/**
* 模拟从数据库取凭证的过程
*
* @param userName
* @return
*/
private String getPasswordByUserName(String userName) {
return userMap.get(userName);
}
引入shiro的相关依赖
Shiro 认证过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BJhJ12w7-1608227209801)(resources/2531156D2E6FB3EC0702F1D1ED795F43.jpg)]
// 1.构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
// 2.主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager); // 设置SecurityManager环境
Subject subject = SecurityUtils.getSubject(); // 获取当前主体
UsernamePasswordToken token = new UsernamePasswordToken("wmyskxz", "123456");
subject.login(token); // 登录
// subject.isAuthenticated()方法返回一个boolean值,用于判断用户是否认证成功
System.out.println("isAuthenticated:" + subject.isAuthenticated()); // 输出true
subject.logout(); // 登出
System.out.println("isAuthenticated:" + subject.isAuthenticated()); // 输出false
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u1IGifmo-1608227209802)(resources/B18B91B5D198827D4140D0CC0A39DBE1.jpg)]
流程如下:
- 首先调用 Subject.login(token) 进行登录,其会自动委托给 Security Manager,调用之前必须通过 SecurityUtils.setSecurityManager() 设置;
- SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证;
- Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现;
- Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;
- Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回 / 抛出异常表示身份验证失败了。此处可以配置多个 Realm,将按照相应的顺序及策略进行访问。
OneBlog开源项目在pom.xml中中引入shiro相关依赖
<!--shiro依赖添加-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.spring.version}</version>
</dependency>
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>${shiro.redis.version}</version>
<exclusions>
<exclusion>
<artifactId>shiro-core</artifactId>
<groupId>org.apache.shiro</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>net.mingsoft</groupId>
<artifactId>shiro-freemarker-tags</artifactId>
<version>${shiro.freemarker.tags.version}</version>
<exclusions>
<exclusion>
<artifactId>fastjson</artifactId>
<groupId>com.alibaba</groupId>
</exclusion>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
<exclusion>
<artifactId>shiro-core</artifactId>
<groupId>org.apache.shiro</groupId>
</exclusion>
</exclusions>
</dependency>