Shiro框架以及Spring Boot整合Shiro

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: Shiro框架以及Spring Boot整合Shiro

一、Shiro框架简介

1.1、shiro介绍

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

Apache Shiro 体系结构

image.png

  1. Authentication 认证 ---- 用户登录
  2. Authorization 授权 --- 用户具有哪些权限
  3. Cryptography 安全数据加密
  4. Session Management 会话管理
  5. Web Integration web系统集成
  6. Interations 集成其它应用,spring、缓存框架

image.png

  1. subject:上面标记为1的是shiro的主体部分subject,可以理解为当前的操作用户
  2. Security Manager:Security Manager为Shiro的核心,shiro是通过security Manager来提供安全服务的,security Manager管理着Session Manager、Cache Manager等其他组件的实例:
  • Authenticator认证器,管理我们的登录登出;
  • Authorizer授权器,负责赋予主体subject有哪些权限;
  • Session Manager是shiro自己实现的一套session管理机制,可以不借助任何web容器的情况下使用session;
  • Session Dao提供了session的增删改查操作;
  • cache Manager缓存管理器,用于缓存角色数据和权限数据;
  • Pluggable Realms是shiro与数据库/数据源之间的桥梁,shiro获取认证信息、权限数据、角色数据都是通过Realms来获取;
  1. cryptography:用来做加密的,使用它可以非常方便快捷的进行数据加密。

  2. 上图组件之间的工作流程:主体提交请求到Security Manager,然后由Security Manager调用Authenticator去做认证,而Authenticator去获取认证数据的时候是通过Realms从数据源中来获取的,然后把从数据源中拿到的认证信息与主体提交过来的认证信息做比对。授权器Authorizer也是一样。

Shiro的核心API:

  • Subject: 用户主体(把操作交给SecurityManager)
  • SecurityManager:安全管理器(关联Realm)
  • Realm:Shiro连接数据库的桥梁

1.2、shiro认证

Shiro的认证流程

  1. 创建Security Manager:Security Manager是用来提供安全服务的,所以在做shiro认证的时候要先创建此对象

  2. 主体Subject提交请求给Security Manager

  3. Security Manager调用Authenticator组件做认证

  4. Authenticator通过Realm来从数据源中获取认证数据

以下通过代码来演示shiro是如何做认证

引入依赖

<dependency>
     <groupId>org.apache.shiro</groupId>
     <artifactId>shiro-core</artifactId>
     <version>1.4.0</version>
</dependency>

<dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>RELEASE</version>
</dependency>
AI 代码解读

使用示例

package com.maltose.shiro.test;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;


/**
 * @auther sgw
 * @create 2019-12-31 14:06
 * @todo 创建一个测试类,测试认证
 */
public class AuthenticationTest {
   
   

    //除了SimpleAccountRealm还有JdbcRealm等可以使用
    SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();

    //在认证之前先在Realm中添加一个用户,来模拟页面传来的用户信息;创建Security Manager的时候要用到Realm
    @Before
    public void addUser() {
   
   
        simpleAccountRealm.addAccount("maltose","123456");
    }

    @Test
    public void testAutentication() {
   
   
        //1.构建Security Manager环境(Security Manager是用来提供安全服务的,所以在做shiro认证的时候要先创建此对象,创建Security Manager对象之后要设置Realm,Realm是前端页面用户提交过来的信息)
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(simpleAccountRealm);

        //2.获取向Security Manager提交请求的subject,而主体subject可以通过shiro提供的一个工具类SecurityUtils来获取
         //使用SecurityUtils之前要设置Security Manager环境
        SecurityUtils.setSecurityManager(defaultSecurityManager);

        Subject subject = SecurityUtils.getSubject();

        //3.主体Subject提交请求给Security Manager -->  subject.login(token);
        //提交请求时需要一个token,所以要先创建token
        UsernamePasswordToken token = new UsernamePasswordToken("xiehuaxin","123456");
        subject.login(token);

        //4. shiro提供了一个检查主体subject是否认证的方法isAuthenticated(),此方法的返回结果是一个boolean值
        System.out.println(subject.isAuthenticated());

        subject.logout();
        System.out.println(subject.isAuthenticated());
    }
}
AI 代码解读

1.3、Shiro的授权

shiro授权流程
shiro授权流程与认证流程基本一致

  1. 创建Security Manager

  2. 主体subject授权

  3. 主体授权提交给Security Manager授权

  4. Security Manager调用授权器Authorizer授权

  5. 通过Realm在数据库或者缓存中来获取授权的数据(角色数据和权限数据)

以下通过代码来演示shiro是如何做授权的

package com.maltose.shiro.test;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
/**
 * @auther sgw
 * @create 2019-12-31 14:15
 * @todo 创建一个测试类,测试授权
 */
public class AuthenticationTest {
   
   

    SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();

    @Before
    public void addUser() {
   
   
        //添加用户的时候为此用户添加角色,一个用户可以拥有一个或多个角色
        simpleAccountRealm.addAccount("maltose", "123456", "admin", "user");
    }

    @Test
    public void testAutentication() {
   
   
        //1.构建Security Manager环境(Security Manager是用来提供安全服务的,所以在做shiro认证的时候要先创建此对象,创建Security Manager对象之后要设置Realm)
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(simpleAccountRealm);
        //2.获取向Security Manager提交请求的subject,而主体subject可以通过shiro提供的一个工具类SecurityUtils来获取
        SecurityUtils.setSecurityManager(defaultSecurityManager);//使用SecurityUtils之前要设置Security Manager环境
        Subject subject = SecurityUtils.getSubject();
        //3.主体Subject提交请求给Security Manager -->  subject.login(token);
        UsernamePasswordToken token = new UsernamePasswordToken("xiehuaxin", "123456");//提交请求时需要一个token,所以要先创建token
        subject.login(token);
        //4. shiro提供了一个检查主体subject是否认证的方法isAuthenticated(),此方法的返回结果是一个boolean值
        System.out.println(subject.isAuthenticated());

        //校验角色
        subject.checkRoles("admin");
    }
}
AI 代码解读

1.3、shiro加密

加密加盐工具类

public class ShiroUtil {
   
   

    /**
     * 生成32的随机盐值
     */
    public static String createSalt(){
   
   
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    /**
     * 加盐加密
     * @param srcPwd    原始密码
     * @param saltValue 盐值
     */
    public static String salt(Object srcPwd, String saltValue){
   
   
        return new SimpleHash("MD5", srcPwd, saltValue, 1024).toString();
    }
}
AI 代码解读

在创建用户时,生成随机盐值,然后将加密后的密码与对应的盐值存入数据库。如:

public class User {
   
   
    /**
     * ID
     */
    private String id;

    /**
     * 登录用户
     */
    @NotEmpty(message = "用户名:用户名不能为空")
    private String username;

    /**
     * 登录密码
     */
    @Length(min = 6, message = "密码:密码长度不能低于6位")
    @NotEmpty(message = "密码:密码不能为空")
    private String password;

    /**
     * 盐值
     */
    private String saltValue;

    /**
     * 手机号
     */
    private String mobile;

    /**
     * 昵称
     */
    private String nickname;

    /**
     * 是否冻结
     */
    private Integer isFrozen;

    /**
     * 创建时间
     */
    private Date createTime;

    public User() {
   
   
    }

    public User(String id, String username, String password, String saltValue, String mobile, String nickname,
                Integer isFrozen, Date createTime) {
   
   
        this.id = id;
        this.username = username;
        this.password = password;
        this.saltValue = saltValue;
        this.mobile = mobile;
        this.nickname = nickname;
        this.isFrozen = isFrozen;
        this.createTime = createTime;
    }

    /**
     * 创建新的用户
     * @param username 用户名
     * @param password 密码
     * @param nickname 昵称
     * @param mobile   手机号
     */
    public static User createUser(String username, String password, String nickname, String mobile){
   
   
        String saleValue = ShiroUtil.createSalt();
        return new User(ShiroUtil.createSalt(), username, ShiroUtil.salt(password, ByteSource.Util.bytes(saleValue).toString()),
                saleValue, mobile, nickname, 0, new Date());
    }
    //省略set/get方法
}
AI 代码解读

shiro进行认证
用户加密完成后,在Shiro中认证时,加盐加密用户输入的密码,然后和库中的对比是否一致即可:

public class MyRealm extends AuthorizingRealm {
   
   

    @Autowired
    private UserMapper userMapper;


    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
   
   
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //todo:获取用户的权限
        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
   
   
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

        User user = this.userMapper.selectByPrimaryKey(token.getUsername());
        if (user == null){
   
   
            throw new AuthenticationException("用户不存在!");
        }
        //盐值
        ByteSource salt = ByteSource.Util.bytes(user.getSaltValue());
        String saltPassword = ShiroUtil.salt(token.getPassword(), salt.toString());
        if (!user.getPassword().equals(saltPassword)){
   
   
            throw new AuthenticationException("输入密码不正确!");
        }
        if (user.getIsFrozen() == 0){
   
   
            throw new AuthenticationException("用户已冻结!");
        }
        //第4个参数是realm名称
        return new SimpleAuthenticationInfo(token.getPrincipal(), token.getPassword(), salt, getName());
    }
}
AI 代码解读

二、Spring Boot整合Shiro实现用户认证

添加整合依赖

<!-- shiro与spring整合依赖 -->
<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring</artifactId>
   <version>1.4.0</version>
</dependency>
AI 代码解读

自定义Realm类

package com.maltose.shiro;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;


/**
 * 自定义Realm
 * @author sgw
 *
 */
public class UserRealm extends AuthorizingRealm{
   
   

    /**
     * 执行授权逻辑
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
   
   
        System.out.println("执行授权逻辑");
        return null;
    }

    /**
     * 执行认证逻辑
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
   
   
        System.out.println("执行认证逻辑");
        return null;
    }
}
AI 代码解读

编写Shiro配置类(*)

package com.maltose.shiro;

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;

/**
 * Shiro的配置类
 * @author sgw
 *
 */
@Configuration
public class ShiroConfig {
   
   

    /**
     * 创建ShiroFilterFactoryBean
     */
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
   
   
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

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

        return shiroFilterFactoryBean;
    }

    /**
     * 创建DefaultWebSecurityManager
     */
    @Bean(name="securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
   
   
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联realm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    /**
     * 创建Realm
     */
    @Bean(name="userRealm")
    public UserRealm getRealm(){
   
   
        return new UserRealm();
    }
}
AI 代码解读

使用Shiro内置过滤器实现页面拦截

package com.maltose.shiro;

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

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;

/**
 * Shiro的配置类
 * @author sgw
 *
 */
@Configuration
public class ShiroConfig {
   
   

    /**
     * 创建ShiroFilterFactoryBean
     */
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
   
   
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

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

        //添加Shiro内置过滤器
        /**
         * Shiro内置过滤器,可以实现权限相关的拦截器
         *    常用的过滤器:
         *       anon: 无需认证(登录)可以访问
         *       authc: 必须认证才可以访问
         *       user: 如果使用rememberMe的功能可以直接访问
         *       perms: 该资源必须得到资源权限才可以访问
         *       role: 该资源必须得到角色权限才可以访问
         */
        Map<String,String> filterMap = new LinkedHashMap<String,String>();
        /*filterMap.put("/add", "authc");
        filterMap.put("/update", "authc");*/

        filterMap.put("/testThymeleaf", "anon");

        filterMap.put("/*", "authc");

        //修改调整的登录页面
        shiroFilterFactoryBean.setLoginUrl("/toLogin");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);


        return shiroFilterFactoryBean;
    }

    /**
     * 创建DefaultWebSecurityManager
     */
    @Bean(name="securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
   
   
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联realm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    /**
     * 创建Realm
     */
    @Bean(name="userRealm")
    public UserRealm getRealm(){
   
   
        return new UserRealm();
    }
}
AI 代码解读

实现用户认证(登录)操作

设计登录页面

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h3>登录</h3>
<form method="post" action="login">
    用户名:<input type="text" name="name"/><br/>
    密码:<input type="password" name="password"/><br/>
    <input type="submit" value="登录"/>
</form>
</body>
</html>
AI 代码解读

编写Controller的登录逻辑


    /**
     * 登录逻辑处理
     */
    @RequestMapping("/login")
    public String login(String name,String password,Model model){
   
   

        /**
         * 使用Shiro编写认证操作
         */
        //1.获取Subject
        Subject subject = SecurityUtils.getSubject();

        //2.封装用户数据
        UsernamePasswordToken token = new UsernamePasswordToken(name,password);

        //3.执行登录方法
        try {
   
   
            subject.login(token);

            //登录成功
            //跳转到test.html
            return "redirect:/testThymeleaf";
        } catch (UnknownAccountException e) {
   
   
            //e.printStackTrace();
            //登录失败:用户名不存在
            model.addAttribute("msg", "用户名不存在");
            return "login";
        }catch (IncorrectCredentialsException e) {
   
   
            //e.printStackTrace();
            //登录失败:密码错误
            model.addAttribute("msg", "密码错误");
            return "login";
        }
    }
AI 代码解读

编写Realm的判断逻辑

package com.maltose.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

/**
 * 自定义Realm
 * @author sgw
 *
 */
public class UserRealm extends AuthorizingRealm{
   
   

    /**
     * 执行授权逻辑
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
   
   
        System.out.println("执行授权逻辑");
        return null;
    }

    /**
     * 执行认证逻辑
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
   
   
        System.out.println("执行认证逻辑");

        //假设数据库的用户名和密码
        String name = "eric";
        String password = "123456";

        //编写shiro判断逻辑,判断用户名和密码
        //1.判断用户名
        UsernamePasswordToken token = (UsernamePasswordToken)arg0;
        if(!token.getUsername().equals(name)){
   
   
            //用户名不存在
            return null;//shiro底层会抛出UnKnowAccountException
        }

        //2.判断密码
        return new SimpleAuthenticationInfo("",password,"");
    }
}
AI 代码解读

整合MyBatis实现登录

导入mybatis相关的依赖

<!-- 导入mybatis相关的依赖 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.9</version>
</dependency>

<!-- mysql -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<!-- SpringBoot的Mybatis启动器 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.1.1</version>
</dependency>
AI 代码解读

配置application.properties

spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

mybatis.type-aliases-package=com.itheima.domain
AI 代码解读

编写User实体

package com.maltose.domain;

public class User {
   
   
    private Integer id;
    private String name;
    private String password;
    public Integer getId() {
   
   
        return id;
    }
    public void setId(Integer id) {
   
   
        this.id = id;
    }
    public String getName() {
   
   
        return name;
    }
    public void setName(String name) {
   
   
        this.name = name;
    }
    public String getPassword() {
   
   
        return password;
    }
    public void setPassword(String password) {
   
   
        this.password = password;
    }
}
AI 代码解读

编写UserMapper接口

package com.maltose.mapper;

import com.maltose.domain.User;

public interface UserMapper {
   
   
    public User findByName(String name);
}
AI 代码解读

编写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">
<!-- 该文件存放CRUD的sql语句 -->
<mapper namespace="com.maltose.mapper.UserMapper">
    <select id="findByName" parameterType="string" resultType="user">
    SELECT     id, 
        NAME, 
        PASSWORD
        FROM 
        user where name = #{value}
    </select>
</mapper>
AI 代码解读

编写业务(service)接口和实现

package com.maltose.service;

import com.maltose.domain.User;

public interface UserService {
   
   
    public User findByName(String name);
}
AI 代码解读

实现

package com.maltose.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.maltose.domain.User;
import com.maltose.mapper.UserMapper;
import com.maltose.service.UserService;

@Service
public class UserServiceImpl implements UserService{
   
   

    //注入Mapper接口
    @Autowired
    private UserMapper userMapper;

    @Override
    public User findByName(String name) {
   
   
        return userMapper.findByName(name);
    }
}
AI 代码解读

启动类添加@MapperScan注解

package com.maltose;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * SpringBoot启动类
 * @author sgw
 *
 */
@SpringBootApplication
@MapperScan("com.maltose.mapper")
public class Application {
   
   

    public static void main(String[] args) {
   
   
        SpringApplication.run(Application.class, args);
    }
}
AI 代码解读

修改UserRealm

package com.maltose.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
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;

import com.maltose.domain.User;
import com.maltose.service.UserService;

/**
 * 自定义Realm
 * @author lenovo
 *
 */
public class UserRealm extends AuthorizingRealm{
   
   

    /**
     * 执行授权逻辑
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
   
   
        System.out.println("执行授权逻辑");
        return null;
    }

    @Autowired
    private UserService userSerivce;

    /**
     * 执行认证逻辑
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
   
   
        System.out.println("执行认证逻辑");

        //编写shiro判断逻辑,判断用户名和密码
        //1.判断用户名
        UsernamePasswordToken token = (UsernamePasswordToken)arg0;

        User user = userSerivce.findByName(token.getUsername());

        if(user==null){
   
   
            //用户名不存在
            return null;//shiro底层会抛出UnKnowAccountException
        }

        //2.判断密码
        return new SimpleAuthenticationInfo("",user.getPassword(),"");
    }
}
AI 代码解读

三、Spring Boot整合Shiro实现用户授权

3.1. 使用Shiro内置过滤器拦截资源

/**
     * 创建ShiroFilterFactoryBean
     */
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
   
   
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

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

        //添加Shiro内置过滤器
        /**
         * Shiro内置过滤器,可以实现权限相关的拦截器
         *    常用的过滤器:
         *       anon: 无需认证(登录)可以访问
         *       authc: 必须认证才可以访问
         *       user: 如果使用rememberMe的功能可以直接访问
         *       perms: 该资源必须得到资源权限才可以访问
         *       role: 该资源必须得到角色权限才可以访问
         */
        Map<String,String> filterMap = new LinkedHashMap<String,String>();
        /*filterMap.put("/add", "authc");
        filterMap.put("/update", "authc");*/

        filterMap.put("/testThymeleaf", "anon");
        //放行login.html页面
        filterMap.put("/login", "anon");

        //授权过滤器
        //注意:当前授权拦截后,shiro会自动跳转到未授权页面
        filterMap.put("/add", "perms[user:add]");

        filterMap.put("/*", "authc");

        //修改调整的登录页面
        shiroFilterFactoryBean.setLoginUrl("/toLogin");
        //设置未授权提示页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);

        return shiroFilterFactoryBean;
    }
AI 代码解读

3.2. 完成Shiro的资源授权

UserRealm:

    /**
     * 执行授权逻辑
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
   
   
        System.out.println("执行授权逻辑");

        //给资源进行授权
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //添加资源的授权字符串
        info.addStringPermission("user:add");

        return info;
    }
AI 代码解读

四、thymeleaf和shiro标签整合使用

4.1. 导入thymeleaf扩展坐标

<!-- thymel对shiro的扩展坐标 -->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>
AI 代码解读

4.2. 配置ShiroDialect

在ShiroConfig类里面添加getShiroDialect方法

    /**
     * 配置ShiroDialect,用于thymeleaf和shiro标签配合使用
     */
    @Bean
    public ShiroDialect getShiroDialect(){
   
   
        return new ShiroDialect();
    }
AI 代码解读

4.3. 在页面上使用shiro标签

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>测试Thymeleaf的使用</title>
</head>
    <body>
        <h3 th:text="${name}"></h3>

        <hr/>

        <div shiro:hasPermission="user:add">
        进入用户添加功能: <a href="add">用户添加</a><br/>
        </div>

        <div shiro:hasPermission="user:update">
        进入用户更新功能: <a href="update">用户更新</a>
        <br/>
        </div>

        <a href="toLogin">登录</a>
    </body>
</html>
AI 代码解读
相关文章
Spring框架初识
Spring 是一个分层的轻量级开源框架,核心功能包括控制反转(IOC)和面向切面编程(AOP)。主要模块有核心容器、Spring 上下文、AOP、DAO、ORM、Web 模块和 MVC 框架。它通过 IOC 将配置与代码分离,简化开发;AOP 提供了声明性事务管理等增强功能。
68 21
Spring框架初识
Spring AI Alibaba 应用框架挑战赛圆满落幕,恭喜获奖选手
第二届开放原子大赛 Spring AI Alibaba 应用框架挑战赛决赛于 2 月 23 日在北京圆满落幕。
对Spring、SpringMVC、MyBatis框架的介绍与解释
Spring 框架提供了全面的基础设施支持,Spring MVC 专注于 Web 层的开发,而 MyBatis 则是一个高效的持久层框架。这三个框架结合使用,可以显著提升 Java 企业级应用的开发效率和质量。通过理解它们的核心特性和使用方法,开发者可以更好地构建和维护复杂的应用程序。
134 29
通过springboot框架创建对象(一)
在Spring Boot中,对象创建依赖于Spring框架的核心特性——控制反转(IoC)和依赖注入(DI)。IoC将对象的创建和管理交由Spring应用上下文负责,开发者只需定义依赖关系。DI通过构造函数、setter方法或字段注入实现依赖对象的传递。Spring Boot的自动配置机制基于类路径和配置文件,自动为应用程序配置Spring容器,简化开发过程。Bean的生命周期包括定义扫描、实例化、依赖注入、初始化和销毁回调,均由Spring容器管理。这些特性提高了开发效率并简化了代码维护。
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
SpringBoot集成Shiro权限+Jwt认证
本文主要描述如何快速基于SpringBoot 2.5.X版本集成Shiro+JWT框架,让大家快速实现无状态登陆和接口权限认证主体框架,具体业务细节未实现,大家按照实际项目补充。
115 11
Spring Boot中的日志框架选择
在Spring Boot开发中,日志管理至关重要。常见的日志框架有Logback、Log4j2、Java Util Logging和Slf4j。选择合适的日志框架需考虑性能、灵活性、社区支持及集成配置。本文以Logback为例,演示了如何记录不同级别的日志消息,并强调合理配置日志框架对提升系统可靠性和开发效率的重要性。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
理解和解决Spring框架中的事务自调用问题
事务自调用问题是由于 Spring AOP 代理机制引起的,当方法在同一个类内部自调用时,事务注解将失效。通过使用代理对象调用、将事务逻辑分离到不同类中或使用 AspectJ 模式,可以有效解决这一问题。理解和解决这一问题,对于保证 Spring 应用中的事务管理正确性至关重要。掌握这些技巧,可以提高开发效率和代码的健壮性。
160 13
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等