【shiro】springboot整合shiro

news2024/9/24 11:25:54

springboot整合shiro,实现用户登录认证,权限校验及rememberMe

1.数据库准备

user 用户表

CREATE TABLE `user` (
  `id` bigint NOT NULL,
  `name` varchar(30) DEFAULT NULL,
  `pwd` varchar(35) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `rid` bigint DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

这里的密码是z3,经过MD5多次加盐后的结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YlUJTftm-1688696284074)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20230707092440322.png)]

role 角色表

CREATE TABLE `role` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `desc` varchar(20) DEFAULT NULL,
  `realName` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

image-20230707092622317

permissions 权限表

CREATE TABLE `permissions` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `info` varchar(20) NOT NULL,
  `desc` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L732BAfj-1688696284075)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20230707094151729.png)]

role_user 角色用户关系表

CREATE TABLE `role_user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `uid` bigint NOT NULL,
  `rid` bigint NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8MLy4G4e-1688696284075)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20230707094238501.png)]

role_ps 角色权限关系表

CREATE TABLE `role_ps` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `rid` bigint NOT NULL,
  `pid` bigint NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aQtIoo0Z-1688696284075)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20230707094316856.png)]

项目结构图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eT9Di6Pf-1688696284076)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20230707094503752.png)]

2.导包

<dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring-boot-web-starter</artifactId>
      <version>1.9.0</version>
    </dependency>

    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-boot-starter</artifactId>
      <version>3.0.5</version>
    </dependency>

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

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

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

    <dependency>
      <groupId>com.github.theborakompanioni</groupId>
      <artifactId>thymeleaf-extras-shiro</artifactId>
      <version>2.0.0</version>
    </dependency>

    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-ehcache</artifactId>
      <version>1.9.0</version>
    </dependency>

    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.5</version>
    </dependency>

3.User实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private Long id;

    private String name;

    private String pwd;

    private Long rid;
}

4.UserMapper类

@Repository
public interface UserMapper extends BaseMapper<User> {

    // 通过用户名称获取角色名称
    @Select("select r.name from role r left join role_user ru on r.id = ru.rid left join user u on ru.uid = u.id where u.name = #{name}")
    List<String> getUserRoles(@Param(value = "name") String name);
    // 通过角色名称获取相关权限
    List<String> getPermissions(@Param(value = "roles") List<String> roles);
}

5.mapper.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 namespace="com.lkx.shiro.mapper.UserMapper">


    <select id="getPermissions" resultType="java.lang.String">
        SELECT p.info FROM permissions p LEFT JOIN role_ps rp ON p.id = rp.pid
        left join role r on r.id = rp.rid
        <where>
            <if test="roles != null and roles.size > 0">
                r.name in
                <foreach collection="roles" item="role" open="(" close=")" separator=",">
                    #{role}
                </foreach>
            </if>
        </where>

    </select>
</mapper>

6.service接口

public interface IUserService extends IService<User>{
    // 通过用户名查询用户信息
    User getUserInfoByName(String name);

    // 通过用户名称获取角色名称
    List<String> getUserRoles(String name);

    // 通过角色名称获取相关权限
    List<String> getPermissions(List<String> roles);
}

7.UserServiceImpl实现类

/**
 * @author likx
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Override
    public User getUserInfoByName(String name) {
        LambdaQueryWrapper<User> eq = new QueryWrapper<User>().lambda().eq(User::getName, name);
        return baseMapper.selectOne(eq);
    }

    @Override
    public List<String> getUserRoles(String name) {
        return this.baseMapper.getUserRoles(name);
    }

    @Override
    public List<String> getPermissions(List<String> roles) {
        return this.baseMapper.getPermissions(roles);
    }
}

8.自定义realm

@Component
@Slf4j
public class MyRealm extends AuthorizingRealm {

    @Autowired
    protected IUserService userService;


    /**
     * 自定义授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("自定义授权");

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        // 获取当前用户的角色
        List<String> userRoles = userService.getUserRoles(principalCollection.getPrimaryPrincipal().toString());

        log.info("roles:{}",userRoles);
        if (CollectionUtils.isNotEmpty(userRoles)) {
            authorizationInfo.addRoles(userRoles);
			// 获取用户拥有的权限
            List<String> permissions = userService.getPermissions(userRoles);
            log.info("permissions:{}",permissions);
            if (CollectionUtils.isNotEmpty(permissions)) {
                authorizationInfo.addStringPermissions(permissions);
            }
        }

        return authorizationInfo;
    }


    /**
     * 自定义登录认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        String name = authenticationToken.getPrincipal().toString();
        User user = userService.getUserInfoByName(name);

        if (Objects.nonNull(user)) {
            AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                    authenticationToken.getPrincipal(),
                    user.getPwd(),
                    ByteSource.Util.bytes("salt"),
                    name);
            return authenticationInfo;
        }


        return null;
    }
}

9.shiro配置类

/**
 * @author likx
 */
@Configuration
public class ShiroConfig {
    @Autowired
    private MyRealm myRealm;

    /**
     * 配置securityManage
     * @return
     */
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager() {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        // 加密对象
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

        // md5加密
        hashedCredentialsMatcher.setHashAlgorithmName("md5");

        // 迭代加密次数
        hashedCredentialsMatcher.setHashIterations(3);

        myRealm.setCredentialsMatcher(hashedCredentialsMatcher);

        manager.setRememberMeManager(rememberMeManager());

        // 使用自定义realm
        manager.setRealm(myRealm);

        return manager;
    }

    /**
     * rememberCookie生成
     * @return
     */
    public SimpleCookie rememberCookie() {
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setPath("/");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(30*24*60*60);
        return cookie;
    }

    /**
     * rememberMeManager 生成
     * @return
     */
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberCookie());
        cookieRememberMeManager.setCipherKey("1234567890987654".getBytes());
        return cookieRememberMeManager;
    }

    @Bean
    public DefaultShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();

        // 设置不认证可访问的资源
        definition.addPathDefinition("/myController/userLogin","anon");
        // 登录过滤
        definition.addPathDefinition("/login","anon");
        // 登出过滤
        definition.addPathDefinition("/logout","logout");
        // 设置需要登录认证的拦截范围
        definition.addPathDefinition("/**","authc");
        definition.addPathDefinition("/**","user");

        return definition;
    }

    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}

10.controller类

@Controller
@RequestMapping("/myController")
@Slf4j
public class MyController {

    @GetMapping("/userLogin")
    public String userLogin(@RequestParam(value = "name") String name,
                            @RequestParam(value = "pwd") String pwd,
                            @RequestParam(name = "rememberMe",defaultValue = "false") Boolean rememberMe,
                            HttpSession session) {
        Subject subject = SecurityUtils.getSubject();
        AuthenticationToken usernamePasswordToken = new UsernamePasswordToken(name, pwd,rememberMe);
        try {
            subject.login(usernamePasswordToken);
            session.setAttribute("user",name);
            log.info("登录成功");
            return "index";
        } catch (Exception e) {
            e.printStackTrace();
            log.info("登录失败");
            return "登录失败";
        }
    }

    @GetMapping("/loginRm")
    public String login(HttpSession session) {
        Object user = session.getAttribute("user");
        log.info("记住我登录:{}",user.toString());
        return "index";
    }

    /**
     * 登录认证角色验证
     */
    @RequiresRoles("admin")
    @GetMapping("/userLoginRoles")
    @ResponseBody
    public String userLoginRoles() {
        log.info("登录认证角色验证");
        return "验证角色成功";
    }
    /**
     * 登录认证权限验证
     */
    @GetMapping("/userLoginPermissions")
    @RequiresPermissions("user:delete")
    @ResponseBody
    public String userLoginPermissions() {
        log.info("登录认证权限验证");
        return "验证权限成功";
    }

    @GetMapping("/login")
    public String login() {
        return "login";
    }
}

11.权限异常处理

@ControllerAdvice
public class PermissionsException {

    @ResponseBody
    @ExceptionHandler(UnauthorizedException.class)
    public String unauthorizedException(Exception e) {
        return "无权限";
    }

    @ResponseBody
    @ExceptionHandler(AuthorizationException.class)
    public String authorizationException(Exception e) {
        return "权限认证失败";
    }
}

12.项目配置


mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/*.xml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/shirodb?charterEncoding=utf-8&useSSL=false
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: root
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
shiro:
  loginUrl: /myController/login
server:
  port: 8081

13.启动类

@SpringBootApplication
@MapperScan("com.lkx.shiro.mapper")
public class ShiroApplication {
    public static void main(String[] args) {
        SpringApplication.run(ShiroApplication.class,args);
    }
}

14.前端页面

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>

<body>
<h1>Shiro登录成功</h1>
<br>
登录用户为:<span th:text="${session.user}"></span>
<br>
<a href="/logout">登出</a>
<br>
<a shiro:hasRole="admin" href="/myController/userLoginRoles">测试授权</a>
<br>
<a shiro:hasPermission="user:delete" href="/myController/userLoginPermissions">测试权限</a>
</body>


</html>

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页</title>
</head>

<body>
<h1>Shiro登录认证</h1>
<br>
<form  action="/myController/userLogin">
    <div>用户名: <input type="text" name="name" value=""></div>
    <div>密码: <input type="password" name="pwd" value=""></div>
    <div>记住我: <input type="checkbox" name="rememberMe" value="true"></div>
    <div><input type="submit" value="登录"></div>
</form>
</body>


</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/732072.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《Java核心技术大会2023》——AIC送书第一期

共同深入探讨 Java 生态&#xff01;直播预约&#xff1a;视频号“IT阅读排行榜” 大会简介 人工智能在22年、23年的再次爆发让Python成为编程语言里最大的赢家&#xff1b;云原生的持续普及令Go、Rust等新生的语言有了进一步叫板传统技术体系的资本与底气。我们必须承认在近…

smale周报

2023.7.7 相关工作近况存在的问题展望 相关工作近况 近期仍就进行3D断层识别实验&#xff0c;实验了很多版本&#xff0c;不同的损失函数和模型&#xff0c;发现当下的效果更好。之前loss一直不正常&#xff0c;改变了之前的一些小问题&#xff0c;使模型和损失函数以及数据匹…

具备捕获 Web2 用户能力的 PoseiSwap,治理通证$POSE再度涨超 360%

Nautilus Chain 是行业内首个模块化 Layer3 架构链&#xff0c;开发者能够基于模块化进行定制化开发&#xff0c;并有望进一步推动 Web3 应用向隐私、合规等方向发展。当然&#xff0c;Nautilus Chain 的特殊之处还在于为生态用户带来丰厚的空投预期&#xff0c;据悉上线 Nauti…

Ubuntu 的移动梦醒了

老实讲&#xff0c;移动版 Ubuntu 在手机、平板上的发展自始至终可能都没有达到过 Canonical 的期望&#xff0c;既然如此&#xff0c;不再勉为其难地坚持下去&#xff0c;或许才是更加明智的做法。 时至今日&#xff0c;官方显然也意识到了这一点&#xff0c;在早些时候发布的…

EPSON机械臂控制记录

我使用的机器人型号为EPSON LS3-4013&#xff0c;在本文中记录了机械臂控制器和电脑连接&#xff0c;及电脑程序控制机械臂的完整流程。 1 USB连接电脑和机械臂 将USB连接电脑和控制器的PC插口。在EPSON RC 软件中选择 设置->电脑和控制器通信。其中默认包括USB通信&#…

标签平滑知识点

一、什么是标签平滑 标签平滑&#xff08;Label smoothing&#xff09;&#xff0c;像L1、L2和dropout一样&#xff0c;是机器学习领域的一种正则化方法&#xff0c;通常用于分类问题&#xff0c;目的是防止模型在训练时过于自信地预测标签&#xff0c;改善泛化能力差的问题。…

OK Print Watch 打印服务监控软件---Crack

O&K Print Watch 是一个服务器端打印监视器。此打印审核工具允许用户控制打印作业、纸张使用情况&#xff0c;并从打印服务器、本地、共享或网络打印机收集统计信息。使用O&K Print Watch&#xff0c;您可以根据许多不同的打印限制为用户定义打印配额。这些功能使用户能…

在Oracle VM VirtualBox 安装ubuntu-22.04.2 后打不开终端处理

1、下载ubuntu-22.04.2-desktop-amd64.iso 下载地址&#xff1a; https://cn.ubuntu.com/download/desktop2、安装 3、直接下一步即可 3、装好系统&#xff0c;发现打不开终端 解决办法&#xff1a;设置->区域与语言 查看语言和格式是否一致&#xff0c;不一致需修改。若…

sqlserver数据库命令行客户端安装

项目情况 项目上需要通过执行sql语句去更新一些功能 直接进入数据库去进行执行太麻烦&#xff0c; 对基础技术人员的要求比较高&#xff0c;执行效率低 参考资料 sqlcmd 实用工具 - SQL Server | Microsoft Learn 下载 ODBC Driver for SQL Server - ODBC Driver for SQL S…

感知机模型

1. 提出问题 问题&#xff1a;有一组训练数据集 T { ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x N , y N ) } T\{(x_1,y_1), (x_2,y_2), \ldots ,(x_N,y_N)\} T{(x1​,y1​),(x2​,y2​),…,(xN​,yN​)} 其中 x i ∈ X R n x_i\in\mathcal{X}R^n xi​∈XRn&#xff0c; y …

Redis【实战篇】---- UV统计

Redis【实战篇】---- UV统计 1. UV统计 - HyperLogLog2. UV统计 - 测试百万数据的统计 1. UV统计 - HyperLogLog 首先我们搞懂两个概念&#xff1a; UV&#xff1a;全称Unique Visitor&#xff0c;也叫独立访客量&#xff0c;是指通过互联网访问、浏览这个网页的自然人。1天内…

Linux信号机制

转自&#xff1a;深入理解Linux信号机制(1.0)_城中之城的博客-CSDN博客 一、信号机制概览 相信大家对信号并不陌生&#xff0c;很多人都用过kill命令或者CtrlC组合键杀死过进程&#xff0c;或者遇到过程序因为收到SIGSEGV信号而崩溃的。而对信号的基本原理&#xff0c;估计很…

DenseNet算法实战

DenseNet算法实战 文章目录 DenseNet算法实战[TOC](文章目录) 前言一、设计理念二、网络结构1.DenseNet网络结构2. DenseBlock Transition结构3. DenseBlock 非线性结构 三、代码实现1. 导入相关的包2. DenseBlock 内部结构3. DenseBlock 模块4. Transition 层5. 最后实现Dens…

23款奔驰S400商务型加装原厂前排座椅通风系统,夏天必备的功能

通风座椅的主动通风功能可以迅速将座椅表面温度降至适宜程度&#xff0c;从而确保最佳座椅舒适性。该功能启用后&#xff0c;车内空气透过打孔皮饰座套被吸入座椅内部&#xff0c;持续时间为 8 分钟。然后&#xff0c;风扇会自动改变旋转方向&#xff0c;将更凉爽的环境空气从座…

TIA博途_封装FB或FC块时将未分配参数的管脚隐藏的具体方法示例

TIA博途_封装FB或FC块时将未分配参数的管脚隐藏的具体方法示例 如下图所示&#xff0c;在某个项目中添加一个模拟量平均值滤波FB块&#xff0c;FB块的输入输出接口如图中所示&#xff0c; FB块编写完成后&#xff0c;在OB1中调用该FB块&#xff0c;可以看到需要配置的相关管脚…

Melon库运用——数组篇

头文件片段 // mln_array.hstruct mln_array_attr {void *pool; // 自定义内存池结构指针array_pool_alloc_handler pool_alloc; // 自定义内存池分配函数指针array_pool_free_handler pool_free; // 自定义内存池释放函数指针array_free …

Linux编译器--gcc/g++的使用

1.gcc/g的作用 gcc/g就是将写好的c/c的代码经过预编译/编译/汇编/链接生成可执行程序的过程&#xff0c;这个过程就是编译器的作用。 PS:由于c支持c语言的语法&#xff0c;gcc和g的操作差不多&#xff0c;在这里只讲gcc的使用方法。 2.gcc如何完成 格式 gcc [选项] 要编译的文…

函数指针数组:更高效的代码实现方式——指针进阶(二)

目录 前言 一、函数指针 什么是函数指针 函数指针的使用 二、函数指针数组 什么是函数指针数组 函数指针数组的使用 三、指向函数指针数组的指针 总结 前言 当谈到C语言的高级特性时&#xff0c;函数指针和函数指针数组通常是最常见的话题之一。虽然这些概念可能会让初…

java面试题(24)

1、重写equals&#xff08;&#xff09;方法的原则 1、对称性&#xff1a; 如果x.equals&#xff08;y&#xff09;返回是“true”&#xff0c;那么y.equals&#xff08;x&#xff09;也应该返回是 “true”。 2、自反性&#xff1a; x.equals&#xff08;x&#xff09;必须…

【动态规划】第N个泰波那契数

&#x1f4ed;从这里开始&#xff0c;我们要开始学习动态规划辣。之后的动态规划有关的文章都是按照这个逻辑来写&#xff0c;首先来介绍一下基本逻辑。 &#x1f9c0;(1)题目解析&#xff1a;就是分析题目&#xff0c;读懂题目想让我们实现的功能 &#x1f9c0;(2)算法原理&…