【SpringBoot】Shiro快速入门(精讲)

一、shiro 简介

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

Shiro可以完成认证、授权、加密、会话管理、web集成、缓存等。


1、shiro の基本功能

Shiro不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。Shiro 可以实现认证、授权、加密、会话管理、与 Web 集成、缓存等。shiro基本功能点如下图所示:
在这里插入图片描述
Authentication:身份认证 / 登录,验证用户是不是拥有相应的角色。

Authorization授权,即权限验证,验证某个已认证/已登录的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限。

Session Management会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的。

Cryptography加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储。

Web SupportWeb 支持,可以非常容易的集成到 Web 环境。

Caching缓存,比如用户登录后,其用户信息、拥有的角色 / 权限不必每次去查,这样可以提高效率。

Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去。

Testing:提供测试支持。

Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。

Remember Me记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

Shiro 不会去维护用户、维护权限;这些需要我们自己去设计 / 提供;然后通过相应的接口注入给 Shiro 即可。


2、shiro外部の系统架构

三个核心组件:Subject, SecurityManager 和 Realms

在这里插入图片描述

Subject:本质上就是当前访问用户的抽象描述。

SecurityManager:是Shiro架构中最核心的组件,通过它可以协调其他组件完成用户认证和授权。实际上,SecurityManager就是Shiro框架的控制器。

Realm:定义了访问数据的方式,用来连接不同的数据源,如:LDAP,关系数据库,配置文件等等。

对于一个简单 Shiro 应用:应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager;我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。从以上也可以看出,Shiro 不提供维护用户 / 权限,而是通过 Realm 让开发人员自己注入,当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。


3、shiro内部の系统架构

在这里插入图片描述
Subject:主体,可以看到主体可以是任何可以与应用交互的 “用户”。

SecurityManager:相当于 SpringMVC 中的 DispatcherServlet ,是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。

Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了。

Authorizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能。

Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,或者内存实现等等;由用户提供;我们一般在应用中都需要实现自己的 Realm。

SessionManager: Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、EJB 等环境;所以Shiro 就抽象了一个自己的 Session 来管理主体与应用之间交互的数据;这样的话,比如我们在 Web 环境用,刚开始是一台 Web 服务器;接着又上了台 EJB 服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到 Memcached 服务器)。

SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD,比如我们想把 Session 保存到数据库,那么可以实现自己的 SessionDAO,通过如 JDBC 写到数据库;另外 SessionDAO 中可以使用 Cache 进行缓存,以提高性能。

CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能。

Cryptography:密码模块,Shiro 提供了一些常见的加密组件用于如密码加密 / 解密的。



二、springboot 整合 shiro 环境搭建

1、导入相の依赖

<!--        shiro整合spring的包(依赖)-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.7.1</version>
</dependency>

<!--        thymeleaf依赖-->
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
</dependency>

2、ShiroConfig配置类の搭建

package com.tiger.config;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author huxuehao
 * @create 2021-08-17-21:51
 */
@Configuration
public class ShiroConfig  {

    //3、ShiroFilterFactoryBean
    // @Qualifier("getDefaultWebSecurityManager") 表示当前类中的getDefaultWebSecurityManager
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();

        //设置安全管理器
        filterFactoryBean.setSecurityManager(securityManager);
        
		此处可以添加shiro的内置过滤器...
		
        return filterFactoryBean;
    }

    //2、DefaultWebSecurityManager 
    // @Qualifier("userRealm") 表示当前类中的 userRealm 方法
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        //关联realm
        securityManager.setRealm(userRealm);

        return securityManager;
    }

    //1、创建 realm对象,自定义 
    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }
}

3、Realm继承类の搭建

package com.tiger.config;

import org.apache.shiro.realm.AuthorizingRealm;

public class UserRealm extends AuthenticatingRealm {
	//授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        System.out.println("【execute】: doGetAuthorizationInfo be executed ...");

        return null;
    }

	//认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)throws AuthenticationException {
    
        System.out.println("【execute】: doGetAuthenticationInfo be executed ...");

       return null;
    }

}


4、前端页面の搭建

我们需要以下一个前端页面(使用的是thymeleaf模板引擎),这个大家在自己写就可以
在这里插入图片描述

5、Controller控制器の搭建

关于后台的路由Controller,如下:

package com.tiger.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class routeController {

    @RequestMapping("/admin/main")
    public String admin(){
        return "pages/admin/adminPage";
    }

    @RequestMapping("/user/main")
    public String user(){
        return "pages/user/userPage";
    }

    @RequestMapping("/tourist/main")
    public String tourist(){
        return "pages/tourist/touristPage";
    }

    @RequestMapping("/login")
    public String login(){
        return "pages/loginPage";
    }
}



三、shiro拦截与认证

我们在开发网站的时候,往往会有如下的需求:对于不同的页面,用户用于不同的角色才能访问,一个页面只有登录之后用户才能访问。

1、实现拦截

在shiro中的拦截,是在ShiroConfig配置类中实现的,至于拦截的相关操作我们都写在ShiroConfig配置类中的getShiroFilterFactoryBean()方法中。shiro中的拦截(认证)级别

anno : 无需认证即可访问

authc : 必须认证才能访问

user : 必须有"记住我"才能访问

perms : 用于对某个资源的权限才能访问

role : 必须拥有某个角色才能访问

//3、ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager securityManager){
	//构造 ShiroFilterFactoryBean 对象
    ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();

    //设置安全管理器
    filterFactoryBean.setSecurityManager(securityManager);

    //添加 shiro 的内置过滤器
    Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); //构造一个map

	//"/user/*","authc" :表示访问/user/下的内容被设置的拦截(认证)级别为authc(必须认证才能访问)
    filterChainDefinitionMap.put("/user/*","authc");
    filterChainDefinitionMap.put("/admin/*","authc");

	//设置过滤器链定义映射,用于实现上面的拦截设置
    filterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

	//当页面被拦截之后,跳到的登录页面的url的设置
    filterFactoryBean.setLoginUrl("/login");

    return filterFactoryBean;
}

2、实现认证

对于 shiro 而言,实现认证我们是在 Realm继承类 中的重写方法中实现的。我们目前还没有连接数据库(整合mybatis),所以现在先试用固定用户名和密码进行认证。

所谓的认证其实就是登录,既然需要登录,那么from表单就要有一个对应的目标路由,我们将shiro中接收username和password以及登录验证的功能写在了from表单对应的目标路由中

    @RequestMapping("/goLogin")
    public String goLogin(String username, String password, Model model){
        //获取当前的用户
        Subject subject = SecurityUtils.getSubject();
        //封装用户数据生成令牌(是一种加密操作)
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);

        try {
            //设置当前用户登录(登录验证)
            subject.login(token);
            //若用户名和密码都成功,那么访问index
            return "index";

        } catch (UnknownAccountException uae) {
            model.addAttribute("msg","用户名不存在");
            return "pages/loginPage";

        } catch (IncorrectCredentialsException ice) {
            model.addAttribute("msg","密码错误");
            return "pages/loginPage";

        } catch (LockedAccountException lae) {
            model.addAttribute("msg","该账户暂时被锁定");
            return "pages/loginPage";

        }
        catch (AuthenticationException ae) {
            model.addAttribute("msg","身份认证异常,请重新登录");
            return "pages/loginPage";
        }
    }

在上述的代码中有UsernamePasswordToken token = new UsernamePasswordToken(username,password)【代码1】subject.login(token)【代码2】这个两段代码,其作用就是现实登录验证,登录时用于验证的用户名和密码就是使用的【代码1】中的username和password。

此时你会有一个疑问,我们并没有设置认证的规则,并不知道正确的用户名和密码,shiro是怎么进行验证的。其实shiro的认证是在 Realm继承类 中实现的,下面我们重写 Realm继承类 中的doGetAuthenticationInfo方法,如下:

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
        throws AuthenticationException {

    System.out.println("【execute】: doGetAuthenticationInfo be executed ...");


    //这个地方就是为什么上段代码和这段代码可以进行联动的原因
    UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
    
    //暂时使用:固定用户名和密码
    String username = "admin";
    String password = "000000";
    

    //用户名认证:如果用户名不存在,那么上段代码会自动捕获异常并抛出异常
    if (!token.getUsername().equals(username)){
        return null;
    }

    //密码认证:密码认证shiro内部自己做,防止密码泄露。如果密码认证失败,那么上段代码会自动捕获异常并抛出异常
    return new SimpleAuthenticationInfo("",password,"");
}

经过上述的两段代码,我们就实现了shiro红的认证功能。


3、测试

①、点击"进入管理员页面"会被拦截,因为未进行身份认证
在这里插入图片描述

②、进行"身份认证"
在这里插入图片描述

③、"身份认证"成功,不会被拦截
在这里插入图片描述

④、访问"进入管理员页面"成功
在这里插入图片描述

四、shiro整合mybatis+druid

上面我们进行认证的时候,使用的是固定的用户名和密码,但是实际开发中,用户的真是用户名和密码(应该进行加密处理)应该是从数据库中取出来了的。下面我们就来打通shiro与数据库的通道。

1、导入 mybatis 相关依赖

<!--        mybatis-spring -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>

<!--        lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
</dependency>


<!--        jdbc-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--        mysql-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<!--        Druid-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.6</version>
</dependency>
<!--        log4j-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

2、DruidConfig配置类の搭建

在这就不过多解释了,有疑惑的朋友可以看我前面写的【SpringBoot】SpringBoot中的Data(jdbc、druid、mybatis)相关整合

package com.tiger.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import javax.sql.DataSource;
import java.util.HashMap;

@Configuration
public class DruidConfig {

    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druidDataSource(){
        return new DruidDataSource();
    }

    //后台监控功能
    //因为springboot内置了servlet容器,所以没有web.xml。替代方法 ServletRegistrationBean
    @Bean
    public ServletRegistrationBean StatViewServlet(){
        //固定写法
        ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>();
        bean.setServlet(new StatViewServlet());
        bean.addUrlMappings("/druid/*");

        //后台需要有人登陆
        HashMap<String , String> initParameters = new HashMap<>();
        //增加配置
        initParameters.put("loginUsername","admin");
        initParameters.put("loginPassword","123456");
        //允许谁能访问
        initParameters.put("allow",""); //("allow","localhost")只允许本机访问;("allow","")允许所有人访问;
        //禁止谁访问 initParameters.put("tiger","192.168.0.1");

        //设置初始化参数
        bean.setInitParameters(initParameters);
        return bean;
    }

    //Filter
    @Bean
    public FilterRegistrationBean b(){
        //固定写法
        FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new WebStatFilter());

        //可以过滤那些请求
        HashMap<String , String > initParameters = new HashMap<>();
        initParameters.put("exclusions","*.js,*.css,/druid/*");

        //设置初始化参数
        bean.setInitParameters(initParameters);
        return bean;
    }
}

3、配置 druid の数据源以及其他配置(application.yaml)

spring:
  datasource:
    username: root
    password: root123123
    url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    # 下面为连接池的补充设置,应用到上面所有数据源中
    # 初始化大小,最小,最大
    initialSize: 5
    minIdle: 5
    maxActive: 20

    # 配置获取连接等待超时的时间
    maxWait: 60000

    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    timeBetweenEvictionRunsMillis: 60000

    # 配置一个连接在池中最小生存的时间,单位是毫秒
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false

    # 打开PSCache,并且指定每个连接上PSCache的大小
    poolPreparedStatements: true
    maxPoolPreparedStatementPerConnectionSize: 20

    # 配置监控统计拦截的 Filter,去掉后监控界面 SQL 无法统计,wall 用于防火墙 日志 log4j
    filters: stat,wall,log4j #导入了log4j
    useGlobalDataSourceStat: true

    # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
    connectProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

4、mybatisの配置(application.yaml)

# 整合mybatis
mybatis:
    type-aliases-package: com.tiger.pojo  # 别名包(别名就是类名)
    mapper-locations: classpath:mybatis/mapper/*.xml  #mapper的位置

5、编写 mapper 以及 mapper.xml

因为在进行身份验证是时,我们一般会通过用户名查密码。

UserMapper:

package com.tiger.mapper;

import com.tiger.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Mapper
@Repository
public interface UserMapper {

    User queryUserByName(String name);

}

UserMapper.xml:(resources/mybatis/mapper/UserMapper.xml)

<?xml version="1.0" encoding="UTF-8" ?>
        <!DOCTYPE mapper
                PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

        <!--绑定Mapper-->
<mapper namespace="com.tiger.mapper.UserMapper">

<select id="queryUserByName" resultType="User" parameterType="String">
        select * from `user` where `name` = #{name}
    </select>
</mapper>



6、service层の编写

UserService:

package com.tiger.service;

import com.tiger.pojo.User;

public interface UserService {
    User queryUserByName(String name);
}

UserServiceImpl:

package com.tiger.service;

import com.tiger.mapper.UserMapper;
import com.tiger.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public User queryUserByName(String name) {
        return userMapper.queryUserByName(name);
    }
}

7、重写 Realm 继承类中のdoGetAuthenticationInfo 方法

@Autowired
UserService userService;

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
        throws AuthenticationException {

    System.out.println("【execute】: doGetAuthenticationInfo be executed ...");


    //这个地方就是为什么上段代码和这段代码可以进行联动的原因
    UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

    //获取数据库信息
    User user = userService.queryUserByName(token.getUsername());

    //用户名认证:如果用户名不存在,那么上段代码会自动捕获异常并抛出异常
    if (!token.getUsername().equals(user.getName())){
        return null;
    }

    //密码认证:密码认证shiro内部自己做,防止密码泄露。如果密码认证失败,那么上段代码会自动捕获异常并抛出异常
    return new SimpleAuthenticationInfo("",user.getPasswd(),"");
}

到此为止,我们是成功的打开了shiro与数据库的通道。



五、shiro 请求授权

我们可能想达到这样一个效果:对于"管理员页面",我们只希望管理员可以访问;对于"用户页面",我们只希望管理员和用户可以访问;对于"游客页面",我们希望管理员、用户、游客都可以访问,那么此时使用到请求授权了。

1、基于资源的权限管理

首先,我们需要给对应的资源加上权限管理,我们在ShiroConfig配置类中的getShiroFilterFactoryBean()实现

之前的拦截是通过如下代码实现的

//"/user/*","authc" :表示访问/user/下的内容被设置的拦截(认证)级别为authc(必须认证才能访问)
filterChainDefinitionMap.put("/user/*","authc");
filterChainDefinitionMap.put("/admin/*","authc");

而我们的基于资源的权限管理的资源权限是通过如下的代码实现的,perms依然具有authc的拦截功能

admin : * : * :admin 表示权限类型,第一个 * 表示操作类型,第一个 * 表示操作的具体条目,这样表示更加清晰,并不是强制要求。如:admin : delete : 01 表示admin具有删除01条目的权限,

perms[…]:中可以添加多个权限(用逗号分隔),当某个用户同时满足perms[...]中所有的权限,才可以访问

//拥有对某个资源的权限才能访问,通过授权才可以
filterChainDefinitionMap.put("/admin/*","perms[admin:*:*]");
filterChainDefinitionMap.put("/user/*","perms[user:*:*]");
filterChainDefinitionMap.put("/tourist/*","perms[tourist:*:*]");

至于资源的授权,我们是在Realm的实现类中的doGetAuthorizationInfo()方法实现的

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

    System.out.println("【execute】: doGetAuthorizationInfo be executed ...");

    //获取AuthorizationInfo对象
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

    //获取当前的用户
    Subject currentUser = SecurityUtils.getSubject();

    //从subject中获取Principal,强转成user
    User user = (User)currentUser.getPrincipal();


    //设置授权(user对应的权限从user对象中取出)
    String[] authorities = user.getAuthority().split(",");//分割权限
    for (String authority : authorities) {
    	//给当前用户添加相应的权限
        authorizationInfo.addStringPermission(authority);
    }

    return authorizationInfo;
}

我们可以发现用户名为admin的用户的authority为admin:*:*,user:*:*,tourist:*:*(如下图)经过split(",")分割之后,就可以得到admin:*:*user:*:*tourist:*:*三条权限
在这里插入图片描述

从上面的代码中,我们可以发现有这么一段代码,如下

//获取当前的用户
Subject currentUser = SecurityUtils.getSubject();

//从subject中获取Principal,因为我们当时存的就是user,所以可以强转成user
User user = (User)currentUser.getPrincipal();

其作用是接收“认证”方法中返回过来的用户的对象(在认证方法中查询数据库得到的),如下

//将user对象保存到subject的principal属性中;
//密码认证shiro做,防止密码泄露
return new SimpleAuthenticationInfo(user, user.getPasswd(),"");

当然你也可以不这么操作,而是在doGetAuthorizationInfo()中再次访问数据库获取到用户的对象。

这样,我们就完成了基于资源的权限管理功能。


2、基于角色的权限管理

首先,我们需要给对应的资源加上基于角色的权限管理,我们在ShiroConfig配置类中的getShiroFilterFactoryBean()实现

roles[…]:中可以添加多个权限(用逗号分隔),当某个用户同时拥有roles[...]中所有的角色,才可以访问

//拥有对某个资源的权限才能访问,通过授权才可以
filterChainDefinitionMap.put("/admin/*","roles[admin]");
filterChainDefinitionMap.put("/user/*","roles[user]");
filterChainDefinitionMap.put("/tourist/*","roles[tourist]");

至于用户的角色添加,我们是在Realm的实现类中的doGetAuthorizationInfo()方法实现的

//授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        System.out.println("【execute】: doGetAuthorizationInfo be executed ...");

        //获取AuthorizationInfo对象
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        //获取当前的用户
        Subject currentUser = SecurityUtils.getSubject();

        //从subject中获取Principal,强转成user
        User user = (User)currentUser.getPrincipal();

        //设置角色(对应的角色从数据库中取)
        String[] roles = user.getRole().split(",");
        for (String role : roles) {
            authorizationInfo.addRole(role);
        }

        return authorizationInfo;
    }

在这里插入图片描述
这样,我们就完成了基于角色的权限管理功能。



六、shiro 开启缓存

没有缓存前:
在这里插入图片描述
有缓存之后:在这里插入图片描述

1、介绍

当我们每次访问被拦截的请求时,都会进行相关的认证和授权,又因为我们在认证和授权的过程中有数据库操作,这就导致计算机资源开销很大。为了避免上述的问题,我们可以使用缓存。

Shiro 提供了类似于 Spring 的 Cache 抽象,即 Shiro 本身不实现 Cache,但是对 Cache 进行了又抽象,方便更换不同的底层 Cache 实现。我们可以在shiro中设置CacheManager,以实现通过第三方缓存来实现shiro的缓存。

接下来,我们将ehcache注入到shiro的CacheManager中,通过ehcache实现shiro的缓存。


2、导入依赖

<!-- shiro-ehcache依赖 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>1.7.1</version>
</dependency>

3、配置shiro中的缓存

配置shiro中的缓存我们是在ShiroConfig配置类中的userRealm()中进行配置的

我们需要设置shiro缓存管理器,因为shiro的缓存默认是关闭的,所以我们需要开启全局缓存,最后需要开启认证、授权缓存并设置缓存名,如下:

@Bean
public UserRealm userRealm(){
    UserRealm userRealm = new UserRealm();

    //设置缓存管理
    userRealm.setCacheManager(new EhCacheManager());//设置shiro缓存管理器
    userRealm.setCachingEnabled(true); //开启全局缓存
    userRealm.setAuthenticationCachingEnabled(true); //开启认证缓存
    userRealm.setAuthenticationCacheName("authenticationCache");
    userRealm.setAuthorizationCachingEnabled(true); //开启授权缓存
    userRealm.setAuthorizationCacheName("authorizationCache");

    return userRealm;
}

至此,我们就开启了shiro中基于ehcache的缓存。



七、shiro 整合 thymeleaf

我们现在想要实现,当登录后,用户的角色是admin,那么"进入管理员页面"、“进入用户页面”、“进入游客员页面"都显示;用户的角色是user,那么"进入用户页面”、"进入游客员页面"被显示;用户的角色是tourist,只显示"进入游客员页面"都显示。


1、导入相关依赖

<!--        thymeleaf依赖-->
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>

<!--  thymeleaf-shiro 整合依赖-->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>

2、引入头文件

在相关的HTML页面引入thymeleaf-shiro头文件

<html lang="en" xmlns:th="http://www.thymeleaf.org"
                xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
    ...
</html>

3、使用shiro标记

判断是否具有某些角色(role)权限,例如:shiro:hasRole=“admin” 的作用是:如果当前的用户具有 admin 角色,那么 shiro:hasRole=“admin” 所在的标签将会被渲染(生效)

<div class="box">
    <h2>【主页】 &nbsp;&nbsp;&nbsp;&nbsp;
        <a th:href="@{/login}">登录</a>
        <a th:href="@{/logout}">注销</a>
    </h2>
    <br>
    <div shiro:hasRole="admin">
        <a th:href="@{/admin/main}">进入管理员页面</a>
    </div>
    <br>
    <div shiro:hasRole="user">
        <a th:href="@{/user/main}">进入用户页面</a>
    </div>
    <br>
    <div shiro:hasRole="tourist">
        <a th:href="@{/tourist/main}">进入游客页面</a>
    </div>
</div>

判断是否具有某些资源(perms)权限,例如:shiro:hasPermission="admin: * : * " 的作用是:如果当前的用户具有 admin: * : * 权限,那么 shiro:hasPermission="admin: * : * " 所在的标签将会被渲染(生效)

<div class="box">
    <h2>【主页】 &nbsp;&nbsp;&nbsp;&nbsp;
        <a th:href="@{/login}">登录</a>
        <a th:href="@{/logout}">注销</a>
    </h2>
    <br>
    <div shiro:hasPermission="admin:*:*">-->
        <a th:href="@{/admin/main}">进入管理员页面</a>
    </div>
    <br>
    <div shiro:hasPermission="user:*:*">-->
        <a th:href="@{/user/main}">进入用户页面</a>
    </div>
    <br>
    <div shiro:hasPermission="tourist:*:*">-->
        <a th:href="@{/tourist/main}">进入游客页面</a>
    </div>
</div>

更新中,尽情期待…



八、实现注册(MD5 + salt 加密)与登录验证

更新中,尽情期待…




当一切都随风而逝的时候,那些特别的瞬间都成了永恒…
在这里插入图片描述

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Shiro 是 Apache 软件基金会的一个开源安全框架,提供了一整套安全管理的解决方案,包括认证、授权、加密和会话管理等功能。Shiro 的目标在于提供简单易用的 API,同时支持高度定制化的需求。 Spring Boot 是一个快速开发的框架,它可以帮助我们快速搭建一个基于 Spring 的应用程序。Spring Boot 的优势在于它内置了很多常用的依赖库和配置,使得开发者可以更加专注于业务逻辑的实现,而不用过多关注底层的配置。 集成 Shiro 和 Spring Boot 可以为我们的应用程序提供强大的安全管理功能,并且可以快速实现这些功能。下面我们将介绍如何在 Spring Boot 中集成 Shiro。 1. 引入 Shiro 依赖 在 pom.xml 文件中添加以下依赖: ``` <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.2</version> </dependency> ``` 这个依赖包含了 Shiro 的核心库以及与 Spring 集成的库。 2. 配置 Shiro 在 Spring Boot 中,我们可以通过在 application.properties 或 application.yml 文件中配置 Shiro。以下是一个示例的 YAML 配置文件: ``` server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/db?useSSL=false&serverTimezone=UTC username: root password: root shiro: loginUrl: /login successUrl: /index filterChainDefinitions: /index = authc ``` 其中 loginUrl 指定了登录页面的 URL,successUrl 指定了登录成功后跳转的 URL,filterChainDefinitions 指定了 URL 的过滤规则。 3. 编写 Shiro 相关代码 在 Spring Boot 中,我们可以使用 Shiro 的注解来实现对方法或 URL 的权限控制。以下是一个示例代码: ``` @RestController @RequestMapping("/api") public class UserController { @RequiresAuthentication @GetMapping("/user/{id}") public User getUser(@PathVariable Long id) { // ... } @RequiresPermissions("user:create") @PostMapping("/user") public void createUser(@RequestBody User user) { // ... } @RequiresRoles("admin") @DeleteMapping("/user/{id}") public void deleteUser(@PathVariable Long id) { // ... } } ``` 在上面的代码中,@RequiresAuthentication 表示需要认证才能访问该方法,@RequiresPermissions("user:create") 表示需要拥有 user:create 权限才能访问该方法,@RequiresRoles("admin") 表示需要拥有 admin 角色才能访问该方法。 4. 启动应用程序 现在我们可以启动应用程序并访问相应的 URL。如果当前用户没有足够的权限访问某个 URL,Shiro 会自动跳转到登录页面,并在登录成功后重定向到相应的 URL。 总结 通过集成 Shiro 和 Spring Boot,我们可以快速构建一个安全的应用程序。在实际开发中,我们可以根据实际需求来配置 Shiro,例如使用自定义的 Realm、加密算法等。通过合理的配置和使用,我们可以提高应用程序的安全性和可靠性。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值