目录
Shiro概述
Shiro介绍
Apache Shiro 是一个功能强大且易于使用的 Java 安全(权限)框架。Shiro 可以完成:认证、授权、加密、会话管理、与 Web 集成、缓存 等。借助 Shiro 您可以快速轻松地保护任何应用程序——从最小的移动应用程序到最大的 Web 和企业应用程序。
官方网址: https://shiro.apache.org/
基本功能
- Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
- Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;
- Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有 信息都在会话中;会话可以是普通 JavaSE 环境,也可以是 Web 环境的;
- Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
- Web Support:Web 支持,可以非常容易的集成到 Web 环境;
- Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可 以提高效率;
- Concurrency:Shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;、
- Testing:提供测试支持;
- Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
- Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了
Shiro架构
- Subject:任何可以与应用交互的“用户”;
- SecurityManager :相当于 SpringMVC 中的 DispatcherServlet;是 Shiro 的心脏; 所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进 行认证、授权、会话及缓存的管理。
- Authenticator:负责 Subject 认证,是一个扩展点,可以自定义实现;可以使用认证 策略(Authentication Strategy),即什么情况下算用户认证通过了;
- Authorizer:授权器、即访问控制器,用来决定主体是否有权限进行相应的操作;即控 制着用户能访问应用中的哪些功能;
- Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体 的;可以是 JDBC 实现,也可以是内存实现等等;由用户提供;所以一般在应用中都需要 实现自己的 Realm;
- SessionManager:管理 Session 生命周期的组件;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境
- CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能
- Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密/解密。
SpringBoot整合Shiro
环境搭建
引入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.9.0</version>
</dependency>
指定登录路径
shiro:
loginUrl: /user/login
创建数据库
user表
role表
user_role表
permissions表
role_permissions表
登录、授权、角色认证实现
省略Mybatis查询数据库代码
自定义实现 Realm
@Component("authorizer")
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
/**
* 自定义授权方法
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//1 创建对象,存储当前登录的用户的权限和角色
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//2 获取当前用户身份信息
String name = principalCollection.getPrimaryPrincipal().toString();
List<String> roles = userService.getUserRoles(name);
//3 存储角色
info.addRoles(roles);
//4 获取用户角色的权限信息
List<String> permissions=new ArrayList<>();
for (String role : roles) {
List<String> ps = userService.getUserPermission(role);
permissions.addAll(permissions);
}
//5 存储权限信息
info.addStringPermissions(permissions);
//6 返回
return info;
}
/**
* 自定义登录方法
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取用户身份信息
String name = authenticationToken.getPrincipal().toString();
//查询用户信息
User user = userService.getUserByName(name);
if (user!=null){
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
name,//用户名
user.getPwd(),//密码
ByteSource.Util.bytes("salt"),//加盐字符
getName()//realm名称
);
return authenticationInfo;
}
return null;
}
}
Shiro配置类
@Configuration
public class ShiroConfig {
//配置自定义Realm
@Bean
public MyRealm myRealm(){
return new MyRealm();
}
//配置 SecurityManager
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
//1 创建 defaultWebSecurityManager 对象
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
//2 创建加密对象,并设置相关属性
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
//2.1 采用 md5 加密
matcher.setHashAlgorithmName("md5");
//2.2 迭代加密次数
matcher.setHashIterations(3);
//3 将加密对象存储到 myRealm 中
myRealm().setCredentialsMatcher(matcher);
//4 将 myRealm 存入 defaultWebSecurityManager 对象
manager.setRealm(myRealm());
//5 返回 defaultWebSecurityManager 对象
return manager;
}
//配置 Shiro 内置过滤器拦截范围
@Bean
public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
//设置不认证可以访问的资源
definition.addPathDefinition("/user/login","anon");
//配置退出过滤器
definition.addPathDefinition("/logout","logout");
//设置需要进行登录认证的拦截范围
definition.addPathDefinition("/**","authc");
return definition;
}
}
controller代码
@RestController
@RequestMapping("/user")
@ResponseBody
public class UserController {
@PostMapping(path = "/login",produces = "application/json")
public String login(@RequestBody User user){
try {
//1 获取 Subject 对象
Subject subject = SecurityUtils.getSubject();
//2 封装请求数据到 token 对象中
UsernamePasswordToken token = new UsernamePasswordToken(user.getName(),user.getPwd());
subject.login(token);
return "登录成功!";
}
catch (LockedAccountException e){
return "账号封禁";
}
catch (UnknownAccountException e) {
e.printStackTrace();
return "用户不存在";
}
catch (IncorrectCredentialsException e) {
e.printStackTrace();
return "密码错误";
}
catch (AuthenticationException e) {
e.printStackTrace();
return "登录失败!";
}
}
//登录认证验证角色
@RequiresRoles("admin")
@GetMapping("/roles")
public String userLoginRoles() {
return "验证角色成功";
}
//登录认证验证权限
@RequiresPermissions("user:delete")
@GetMapping("/permissions")
public String userLoginPermissions() {
return "验证权限成功";
}
}
权限异常处理
@ControllerAdvice
@ResponseBody
public class PermissionsException {
@ExceptionHandler(UnauthorizedException.class)
public String unauthorizedException(Exception ex){
return "无权限";
}
}
多个 realm 的认证策略设置
AuthenticationStrategy class | 描述 |
---|---|
AtLeastOneSuccessfulStrategy | 只要有一个(或更多)的 Realm 验证成功,那么认证将视为成功 |
FirstSuccessfulStrategy | 第一个 Realm 验证成功,整体认证将视为成功,且后续 Realm 将被忽略 |
AllSuccessfulStrategy | 所有 Realm 成功,认证才视为成功 |
ModularRealmAuthenticator 内置的认证策略默认实现是 AtLeastOneSuccessfulStrategy 方式。
//配置 SecurityManager
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
//1 创建 defaultWebSecurityManager 对象
DefaultWebSecurityManager defaultWebSecurityManager = new
DefaultWebSecurityManager();
//2 创建认证对象,并设置认证策略
ModularRealmAuthenticator modularRealmAuthenticator = new
ModularRealmAuthenticator();
modularRealmAuthenticator.setAuthenticationStrategy(new
AllSuccessfulStrategy());
defaultWebSecurityManager.setAuthenticator(modularRealmAuthenticator);
//3 封装 myRealm 集合
List<Realm> list = new ArrayList<>();
list.add(myRealm1);
list.add(myRealm2);
//4 将 myRealm 存入 defaultWebSecurityManager 对象
defaultWebSecurityManager.setRealms(list);
//5 返回
return defaultWebSecurityManager;
会话管理
SessionManager由SecurityManager管理。Shiro提供了三种实现:
- DefaultSessionManager:用于JavaSE环境
- ServletContainerSessionManager:用于web环境,直接使用Servlet容器的会话
- DefaultWebSessionManager:用于web环境,自己维护会话(不使用Servlet容器的
会话管理)
获得session方式
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute(“key”,”value”)
- Controller 中的 request,在 shiro 过滤器中的 doFilerInternal 方法,被包装成
ShiroHttpServletRequest。 - SecurityManager 和 SessionManager 会话管理器决定 session 来源于ServletRequest还是由 Shiro 管理的会话。无论是通过 request.getSession 或subject.getSession 获取到 session,操作session,两者都是等价的。
学习内容来自尚硅谷