哔哩哔哩:狂神说 Java

一、Shiro 简介?

Shiro 是一个 Java 的安全框架,不依赖任何容器,可以运行在 Java SE 和 Java EE 项目中,它的主要作用是对访问系统的用户进行身份认证,授权,会话管理,加密操作。

没有 Shiro 的时候,我们进行用户验证,可以使用过滤器,判断 session 是否存在

Shiro 是用来解决安全管理的系统化框架

1.1 什么是权限

  • 在 Web 系统中我们经常涉及到权限问题,例如不同角色的人登录系统,他操作的功能,按钮,菜单是各不相同的。这就是所谓的权限

1.2 权限校验

  • 用户认证:用户身份识别,即登录
  • 用户授权:访问控制
  • 密码加密:加密敏感数据防止被偷窥
  • 会话管理:与用户相关的时间敏感的状态信息

1.3 Shiro 的功能

  • 可以在 Java SE 环境使用,也可以在 Java EE 环境使用
  • 帮助我们完成:认证、授权、加密、会话管理、与web 集成、缓存

二、Shiro 核心组件及用户认证

2.1 Shiro 主要概念

用户,角色,权限

  • 给角色赋予权限
  • 给用户赋予角色

  • Subject: 就是与系统交互的当前“用户”,用户不仅仅是人,也可以是第三方服务,爬虫等正在与系统交互的任何事物

  • SecurityManager:是 Shrio 框架的核心,协调内部各个安全组件之间的交互
  • Realms:在 Shrio 和用户的应用程序之间扮演着桥梁和连接器的作用。当需要验证或者授权的时候,Shrio 从一个或者多个配置的 Realms 中查找

2.2 Shiro 核心 API

Shiro API 功能
UsernamePasswordToken Shrio 用来封装用户的的登录信息,使用用户登录信息来创建令牌
SecurityManager 相当于 SpringMVC 中的DispatcherServlet 或者 Struct2 中的 FiliterDispatcher;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有的 Subject ;负责安全认证与授权、及会话、缓存的管理。
Subject Shiro 的一个抽象概念,包含了用户信息。可以有一个或者多个 Realm,可以认为是安全试题数据源,既用于获取安全实体的;也可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意 Shiro不知道你的用户/权限存储在哪以及何种格式存储;所以我们一般在应用中都需要实现自己的 Realm
Realm 开发者根据项目需求自定义的模块,验证和授权的逻辑全部写在Realm
AuthenticationInfo 用户角色信息集合。认证时使用
AuthorzationInfo 角色权限信息集合,授权时使用
DefaultWebSecurityManager 安全管理器开发者自定义的 Realm 需要到 DefaultWebSecurityManager,进行管理才能生效
ShiroFilterFactoryBean 过滤器工厂,Shiro 的基本运行机制是开发者定义的规则,Shiro 去执行,具体的执行操作就是由 ShiroFilterFactoryBean 创建的一个个 Filter 对象创建完成
Authenticator 认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了
Authorizer 授权器:或者访问控制器,用来决定主体是否有权限进行相应的操作,即控制着用户能访问应用中哪些功能

运行机制
image.png

2.3 用户认证流程

  1. 首先调用Subject.login(token)进行登录, 其会自动委托给Security Manager,调用之前必须通过SecurityUtis. setSecurityManager0设置;
  2. SecurityManager负责真正的身份验证逻辑: 它会委托给Authenticator进行身份验证;
  3. Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现;
  4. Authenticator可能会委托给相应的AuthenticationStrategy 进行多 Realm 身份验证,默认ModularRealmAuthenticator会调用 AuthenticationStrategy 进行多Realm身份验证;
  5. Authenticator会把相应的token传入Realm, 从Realm获取身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。

2.4 常见异常

image.png

2.5 用户认证常用方法

  1. //获取当前用户
  2. Subject currentUser = SecurityUtils.getSubject();
  3. // 判断用户已经认证
  4. currentUser.isAuthenticated()
  5. // 用户登录
  6. UsernamPasswordToken token = new UsernamePassword("admin","123312");
  7. // 记住我
  8. token.setRememberMe(true)
  9. currentUser.login(token);
  10. // 注解
  11. @RequiresPermissions 要求当前 Subject 在执行被注解的方法时具备一个或多个对应的权限。
  12. @RequiresRoles 要求当前 Subject 在执行被注解的方法时具备所有的角色,否则将抛出 AuthorizationException 异常
  13. @RequiresAuthentication 要求在访问或调用被注解的类、实例、方法时,Subejct 在当前session 中已经被验证

三、用户授权流程

3.1 概念

授权(Authorization)也叫做访问控制,是一个对资源的访问进行管理的过程,也就是说在应用程序汇总,谁有怎样的权限(用户可以看到什么内容,可以进行什么操作)。在授权中需要了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)

四、自定义 Realm

五、过滤器集

四、SpringBoot 整合 Shiro

4.1 环境搭建以及测试

  1. 创建 SpringBoot 应用,集成 Shiro 坐标依赖,和其他相关依赖

     <dependencies>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-thymeleaf</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
    
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
             <optional>true</optional>
         </dependency>
    
         <!-- SpringBoot 整合 Shiro -->
         <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
         <dependency>
             <groupId>org.apache.shiro</groupId>
             <artifactId>shiro-spring</artifactId>
             <version>1.5.3</version>
         </dependency>
    
         <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
             <version>5.1.10</version>
         </dependency>
    
         <!-- 使用 mybatis plus -->
         <dependency>
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-boot-starter</artifactId>
             <version>3.2.0</version>
         </dependency>
    
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
             <exclusions>
                 <exclusion>
                     <groupId>org.junit.vintage</groupId>
                     <artifactId>junit-vintage-engine</artifactId>
                 </exclusion>
             </exclusions>
         </dependency>
     </dependencies>
    
  2. 测试项目环境是否完整

进入命令行,输入 mvn clean install 命令,当看到 build success 的时候,说明项目搭建完毕

  1. 自定义 Shiro 过滤器

3.2 Shiro 相关配置类编写

  1. 编写Realm,使用 Shiro 完成账号密码的验证,并生成 token ```java package com.gorit.realm;

import com.gorit.entity.Account; import com.gorit.service.AccountService; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.beans.factory.annotation.Autowired;

/**

  • Shiro 自定义 realm
  • 登录操作,过来权限的判断 */ public class AccountRealm extends AuthorizingRealm {

    @Autowired private AccountService accountService;

    /**

    • 用户权限信息集合,授权时使用 *
    • @param principalCollection
    • @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; }

      /**

    • 用户角色信息,认证时使用
    • 过程:
      1. 客户端传送过来的账号 和 密码,会封装到 token 里面
      1. 然后验证账户是否存在
      1. 账户存在就返回 SimpleAuthenticationInfo 对象 在判断传过来的密码和 token 里面的是否一致
    • @param authenticationToken
    • @return
    • @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { // 获得 token UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; // token.getUsername() Account account = accountService.findByUsername(token.getUsername()); // 验证账号是否存在 if (account!=null) {
       // 密码验证
       return new SimpleAuthenticationInfo(account,account.getPassword(),getName());
      
      } return null; } } ```
  1. 编写 Shiro 相关配置类 ```java package com.gorit.config;

import com.gorit.realm.AccountRealm; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;

/**

  • Shiro 的相关配置写在这里,权限认证做好了,就要编写配置
  • realm 注入到 manager
  • manager 注入到 */

@Configuration public class ShiroConfig {

// 创建一个对象,并丢入到 SpringIoc 容器中
@Bean
public AccountRealm accountRealm() {
    return new AccountRealm();
}

// 安全管理器配置
@Bean
public DefaultWebSecurityManager securityManager(@Qualifier("accountRealm") AccountRealm accountRealm) {
    // 创建对象
    DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
    // 将注入到 IOC 的 Realm 取出来
    manager.setRealm(accountRealm);
    return manager;
}

// 配置过滤器工厂
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
    ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
    factoryBean.setSecurityManager(securityManager);
    return factoryBean;
}

}

```

3.3 Shiro 编写认证和授权规则

  • 认证过滤器

    • anon:无需认证
    • authc:必须认证
    • authcBase:需要通过 HTTPBasic 认证
    • user:不一定通过认证,只要曾经被 Shiro 记录即可,比如记住我
  • 授权过滤器

    • perms:必须拥有某个权限才能访问
    • role:必须拥有某个角色才能访问
    • port:请求的端口必须是指定值才可以
    • rest:请求必须基于 RESTful POST GET DELETE PUT
    • ssl:必须是安全的 URL 请求,协议 https

创建三个页面:main.html、manage.html、administrator.html

  1. 必须登录才能访问 main.html
  2. 当前用户必须拥有 manage 授权才能访问 manage.html
  3. 当前用户必须用用 administrator 角色才能访问 administrator.html