SpringBoot整合Shiro

news2024/11/19 22:39:47

简介

Shiro是一个功能强大和易于使用的Java安全框架,为开发人员提供一个直观而全面的解决方案的认证,授权,加密,会话管理。

Shiro 四个主要的功能:

  • Authentication:身份认证/登录,验证用户是不是拥有相应的身份

  • Authorization:授权,即权限验证,判断某个已经认证过的用户是否拥有某些权限访问某些资源,一般授权会有角色授权和权限授权

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

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

Shiro 的其它几个特点:

  • Web Support:Web支持,可以非常容易的集成到Web环境;

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

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

  • Testing:提供测试支持;

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

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

Shiro 的架构有 3 个主要概念:Subject, SecurityManager和Realms

  • Subject:主体,相当于是请求过来的“用户”

  • SecurityManager: 管理着所有 Subject,负责进行认证和授权、及会话、缓存的管理,是 Shiro 的心脏,所有具体的交互都通过 SecurityManager 进行拦截并控制

  • Realm:一般我们都需要去实现自己的Realm ,可以有1个或多个 Realm,即当我们进行登录认证时所获取的安全数据来源(帐号/密码)

环境搭建

  1. 创建SpringBoot项目springboot-shiro-first,导入Shiro依赖包

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.7.1</version>
        </dependency>
  1. 编写简单的前端代码

index.html

<!DOCTYPE html >
<html lang="en" xmlns:th="http://www.thymeleaf.org" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
<a href="/user/add">add</a> |
<a href="/user/update">update</a>
</body>
</html>

add.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>add</h1>
</body>
</html>

update.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>update</h1>
</body>
</html>
  1. 编写controller

@RestController
public class UserController {
 @RequestMapping({"/","/index"})
    public String toIndex(Model model) {
        model.addAttribute("msg","hello, Shiro!");
        return "index";
    }

    @RequestMapping("/user/add")
    public String add() {
        return "user/add";
    }

    @RequestMapping("/user/update")
    public String update() {
        return "user/update";
    }
}
  1. 编写Shiro配置类ShiroConfig

package com.tomato.jackson.config;

import com.tomato.jackson.entity.UserRealm;
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 gf
 * @date 2023/1/30
 */
@Configuration
public class ShiroConfig {
  // ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);
        return bean;
    }

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

    // 创建 realm 对象, 需要自定义类
    // @Bean(name = "userRealm")
    @Bean
    public UserRealm userRealm() {
        return new UserRealm();
    }
}

注意:

  • 以上代码为Shiro配置类的固定写法,需要创建ShiroFilterFactoryBean过滤器对象、DefaultWebSecurityManager对象、自定义Realm对象

  • 通过@Qualifier标签拿到userRealm()方法创建的Bean对象,绑定到getDefaultWebSecurityManager()方法中的参数userRealm上

  1. 编写自定义的Realm类,需要继承AuthorizingRealm抽象类

package com.tomato.jackson.entity;

import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

/**
 * @author gf
 * @date 2023/1/30
 */
public class UserRealm extends AuthorizingRealm {
    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了=>授权doGetAuthorizationInfo");
        return null;
    }

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了=>认证doGetAuthenticationInfo");
        return null;

    }

}
  1. 测试页面跳转

首页:

新增(add):

更新(update):

登录拦截

果点击add、update时,要设置拦截功能:在进行页面跳转过程中首先会跳转到一个登录界面。Shiro可以通过拦截器链实现该功能,常见的拦截器有:

  • anon:任何人都可以访问

  • authc:只有认证后才可以访问

  • logout:只有登录后才可以访问

  • roles[角色名]:只有拥有特定角色才能访问

  • perms["行为"]:只有拥有某种行为的才能访问

代码实现步骤如下:

  1. 手动创建拦截跳转页面

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>登录</title>
    <!--semantic-ui-->
    <link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
</head>
<body>

<!--主容器-->
<div class="ui container">

    <div class="ui segment">

        <div style="text-align: center">
            <h1 class="header">登录</h1>
        </div>

        <div class="ui placeholder segment">
            <div class="ui column very relaxed stackable grid">
                <div class="column">
                    <div class="ui form">
                        <!--<form th:action="@{/login}" method="post">-->
                        <form action="/login" method="post">
                            <div class="field">
                                <label>Username</label>
                                <div class="ui left icon input">
                                    <input type="text" placeholder="username" name="username">
                                    <i class="user icon"></i>
                                </div>
                            </div>
                            <div class="field">
                                <label>Password</label>
                                <div class="ui left icon input">
                                    <input type="password" name="password">
                                    <i class="lock icon"></i>
                                </div>
                            </div>
                            <input type="submit" class="ui blue submit button"/>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script th:src="@{/js/jquery-3.1.1.min.js}"></script>
<script th:src="@{/js/semantic.min.js}"></script>

</body>
</html>
  1. 并编写相应controller

    @RequestMapping("/toLogin")
    public String toLogin() {
        return "去登陆";
    }
  1. 编写登录拦截功能

登录拦截功能代码的实现需要在ShiroConfig类中getShiroFilterFactoryBean(...)添加以下代码:


        // (1)设置拦截器链 : anon authc logout roles perms
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/user/**","authc");
        bean.setFilterChainDefinitionMap(filterMap);
        // (2)设置拦截登录的请求(拦截之后跳转的页面)
        bean.setLoginUrl("/toLogin");
  • filterMap.put("/user/**","authc");表示路径user下的所有请求必须通过认证才会跳转

  • bean.setLoginUrl("/toLogin");表示路径user下的所有请求会跳转到URL为/toLogin的登录页面进行认证

  1. 测试拦截功能

点击add/update提示去登录

如果删除代码:

// (2)设置拦截登录的请求(拦截之后跳转的页面)
bean.setLoginUrl("/toLogin");

点击add、update时,跳转失败:

用户认证

用户在跳转页面时需要经过验证,否则不能跳转到界面里,登录拦截实现了拦截功能,此时需要在登录界面输入相应的用户名和密码进行验证,来判断输入的用户是否可以通过验证并进行界面跳转。步骤如下:

  1. 前端登录界面输入用户名和密码跳转到相应的controller进行处理。编写一个对应的controller

    @PostMapping("/login")
    public String login(@RequestParam("username") String username, @RequestParam("password")String password) {

        // 获取当前用户
        Subject subject = SecurityUtils.getSubject();
        // 封装用户的登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        // 执行登录方法
        try {
            subject.login(token);
            return "index";
        } catch(UnknownAccountException e) { //用户名不存在
            return "login";
        } catch (IncorrectCredentialsException e) { // 密码不存在
            return "login";
        }
    }

此时进行登录测试会发现自动执行了UserRealm类中的doGetAuthenticationInfo(...)认证方法

2023-01-31 14:13:00.225  INFO 16612 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-01-31 14:13:00.226  INFO 16612 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2023-01-31 14:13:00.226  INFO 16612 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
执行了=>认证doGetAuthenticationInfo
  1. 在UserRealm类补全的doGetAuthenticationInfo(...)认证方法代码

// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    System.out.println("执行了=>认证doGetAuthenticationInfo");

    // 模拟获取数据库中的用户名和密码
    String uname = "root";
    String pwd = "123456";
    UsernamePasswordToken userToken = (UsernamePasswordToken) token;
    System.out.println(userToken.getUsername());
    // 验证用户名是否输入正确
    if (!userToken.getUsername().equals(uname)) {
        // 用户名输入错误, return null 表示抛出异常 UnknownAccountException
        return null;
    }
    // 验证密码是否输入正确, 由 Shiro 自动完成
    return new SimpleAuthenticationInfo("", pwd, "");
}
  1. 测试登陆效果

根据登录逻辑,如果用户名和密码正确返回index,否则返回login

注意:SimpleAuthenticationInfo()中传入的密码需要String类型,传入int类型认证失败

用户授权

如果需要实现部分用户可以访问add页面,部分页面可以访问update页面,需要用到用户授权功能,实现步骤如下:

  1. 在ShiroConfig配置类getShiroFilterFactoryBean(...)方法中添加如下代码:

// 授权// 携带 user:add 字符串的用户才能有权限访问 user 文件夹下的 add 页面
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");

表示如果想访问/user文件夹下的add资源,需要用户具有user:add权限

之后登录认证成功之后输入正确的用户名和密码发现报错未授权,错误401

此时执行了UserRealm类中的授权方法

需要在UserRealm类中的授权方法编写授权代码

  1. 用户未授权可以跳转到自定义未授权页面,而不是401页面

ShiroConfig类getShiroFilterFactoryBean(...)方法添加:

// 设置未授权跳转页面
bean.setUnauthorizedUrl("/unauth");

类MyController添加:

@RequestMapping("/unauth")@ResponseBodypublicStringunauth(){return"用户未授权访问权限!";}

测试如果用户未授权跳转到以下界面:

  1. 在UserRealm类中的授权方法编写授权代码

// 授权@OverrideprotectedAuthorizationInfodoGetAuthorizationInfo(PrincipalCollection principalCollection){System.out.println("执行了=>授权doGetAuthorizationInfo");SimpleAuthorizationInfo info =newSimpleAuthorizationInfo();// 为所有用户增加 user:add 权限//info.addStringPermission("user:add");// 获取当前登录用户Subject subject =SecurityUtils.getSubject();// 获取认证方法中查到的当前登录用户信息 : User 对象User currentUser =(User) subject.getPrincipal();// 获取当前用户在数据库中查询到的拥有的权限, 并为当前用户设置该权限
    info.addStringPermission(currentUser.getPerms());return info;}
  • 对象subject通过getPrincipal()方法获得当前User用户,User用户的信息是在认证代码中数据库查询到的,修改认证代码如下所示,通过return返回了一个SimpleAuthenticationInfo对象,将user对象作为SimpleAuthenticationInfo对象的第一个参数传入,之后授权代码便可以通过getPrincipal()方法获得当前User用户的信息了。

// 认证@OverrideprotectedAuthenticationInfodoGetAuthenticationInfo(AuthenticationToken token){
	// ...returnnewSimpleAuthenticationInfo(user, user.getPwd(),"");}
  • 通过addStringPermission()为当前User用户设置与数据库对应一致的权限,数据库表usetest增加权限字段perms,对应的User实体类也增加perms属性

@Data//Lombok标签@AllArgsConstructor@NoArgsConstructorpublicclassUser{//属性名称要与数据库对应表字段名称一致(不区分大小写)privateint id;privateString username;privateString pwd;privateString perms;}

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

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

相关文章

kubernetes -- 删除namespace的过程以及遇到的bug解决

通过阅读本篇文章你可以收获如下知识&#xff1a; 解决一个bug。理解k8s的controller中&#xff0c;删除namespace的源码&#xff0c;理解其中的删除过程。 问题 执行kubectl delete ns {ns-name}命令来删除ns-name的时候&#xff0c;发现状态一直停留在Terminating。 [roo…

2023牛客寒假算法基础集训营5

(0条未读通知) 【题解】2023牛客寒假算法基础集训营5_ICPC/CCPC/NOIP/NOI刷题训练题单_牛客竞赛OJ (nowcoder.com) A-小沙の好客 下面是错误代码&#xff0c;我刚开始看到这题&#xff0c;觉得很简单阿&#xff0c;做了才知道&#xff0c;会超时&#xff0c;所以不能单纯的去做…

Mac电脑运行速度又卡又慢如何解决?CleanMyMacX2023最新版

CleanMyMac X为您喜爱的东西腾出空间。体验一系列巧妙的新功能&#xff0c;CleanMyMac可让您安全智能地扫描和清理整个系统&#xff0c;删除大量未使用的文件&#xff0c;缩小iPhoto图库的大小&#xff0c;卸载不需要的应用程序或修复不正常工作的应用程序&#xff0c;管理所有…

ARM异常处理

1.ARM异常源1.1 异常模式2.ARM异常响应CPSR内存储的是当前模式。2.1 异常向量表每个异常源只有四个字节的存储空间&#xff0c;所以不在向量表中写异常处理程序&#xff0c;而是写跳转指令。2.2 IRQ异常举例

产业互联网曾被认为是一个生搬硬凑出来的概念

有关产业互联网的质疑&#xff0c;依然还在耳畔会响。但&#xff0c;我们却无可避免地进入到了产业互联网的周期里。这听上去有些耸人听闻&#xff0c;却在真实地发生着。曾经&#xff0c;产业互联网被认为是一个生搬硬凑出来的概念&#xff0c;甚至还有人认为&#xff0c;它仅…

垃圾渗滤液膜后稳定出水水质稳定工艺

垃圾渗滤液的来源及水质特点 垃圾渗滤液是垃圾在堆放和填埋过程中由于发酵和雨水的淋洗、冲刷以及地表地下水的浸泡出来的污水。 当垃圾堆体的湿度超过其持水能力后&#xff0c;垃圾堆体内悬浮的或溶解的有机污染物和重金属等无机污染物将会随之一块溶出&#xff0c;便会产生渗…

大学生需要配备什么电子产品、上大学必备电子产品推荐清单

又是一年开学季&#xff0c;最近我们这里的中小学已经陆陆续续通知开学了。大学生正式开学还需要一段时间&#xff0c;作为大学生在入学之前&#xff0c;一定要准备好各种学习和生活物资。对于大学生而言&#xff0c;有大把的时间可以用来快活&#xff0c;而现在数码产品又成为…

中国电子学会2022年06月份青少年软件编程Scratch图形化等级考试试卷四级真题(含答案)

2022-06 Scratch四级真题 分数&#xff1a;100 题数&#xff1a;29 测试时长&#xff1a;100min 一、单选题(共15题&#xff0c;共30分) 1.执行下列程序&#xff0c;说的内容是&#xff1f;&#xff08;D&#xff09;(2分) A.使 B.命 C.初 D.心 答案解析&#xff1a;注…

Golang微服务基础技术

单体式和微服务 单体式架构服务 ------过往大家熟悉的服务器 特性&#xff1a; 1.复杂性随着开发越来越高&#xff0c;遇到问题解决困难。 2. 技术债务逐渐上升 3. 耦合度高&#xff0c;维护成本大 出现bug&#xff0c;不容易排查 解决旧bug&#xff0c;会出新bug 4. 持续交付…

“深度学习”学习日记。卷积神经网络--整体结构

2023.2.1 一、全连接层神经网络&#xff1a; 之前学习的神经网络称为 全连接神经网络 &#xff08; Fully-Connected&#xff09;&#xff0c;它的特点就是相邻层所有的神经元都有联接&#xff0c;通过Affine层实现全连接层。 在全连接层神经网络中&#xff0c;Affine层后面…

【ROS-Navigation】—— DWA路径规划算法解析

文章目录前言1. 涉及的核心配置文件与启动文件1.1 demo01_gazebo.launch1.2 nav06_path.launch1.3 nav04_amcl.launch1.4 nav05_path.launch1.5 move_base_params.yaml1.6 dwa_local_planner_params.yaml2. 调参时的一些经验与心得2.1 DWA算法流程2.2 对costmap的参数进行调整2…

1. Mybatis基础

文章目录1. Mybatis 简介2. Mybatis 快速入门3. 使用 idea 写 SQL4. Mapper 代理开发5. MyBatis 核心配置文件1. Mybatis 简介 MyBatis 是一款优秀的持久层框架&#xff0c;用于简化 JDBC 开发。 官方文档&#xff1a;https://mybatis.org/mybatis-3/zh/index.html 持久层&am…

GoogLeNet——网络实战

文章目录摘要&#x1f407;1 项目结构&#x1f407;2 划分训练集和测试集&#x1f407;3 计算mean和Standard&#x1f407;3.1 标准化的作用&#x1f407;3.2 归一化的作用&#x1f407;4 训练&#x1f407;4.1 导入项目使用的库&#x1f407;4.2 设置随机因子&#x1f407;4.3…

Java单例模式演示与理解

目录单例模式1、饿汉式2、懒汉式3、DSL懒汉式&#xff08;双重锁懒汉模式&#xff09;静态内部类懒汉式单例模式的如何破坏4、使用枚举类单例模式 为什么使用单例模式&#xff1f; 单例模式确保一个类在内存中只会有同一个实例对象存在。不管声明获取实例对象多少次都是内存中…

2023年网络安全八大预测!

随着创新技术的不断兴起&#xff0c;以及网络犯罪的日益专业化&#xff0c;网络安全攻击风险仍在持续增长。可以预见&#xff0c;2023年的网络安全形势依然严峻&#xff0c;需要国家不断完善网络安全政策和法规&#xff0c;网络安全企业积极创新网络安全防护技术。瑞数信息作为…

Allegro如何导出和导入器件模型Signal_Model操作指导

Allegro如何导出和导入器件模型Signal_Model操作指导 在用Allegro做PCB设计的时候,通常需要给器件加上Signal_Model,在做等长的时候用到的非常频繁。 Allegro除了可以给器件添加模型,还支持从一块加好模型的BRD导入到另外一块BRD中, 如下图,需要把R7002的Signal_Model导入…

剪报浏览器:可以自己设计网页的浏览器

总的功能就是一句话“不同网站的精华内容裁剪下来&#xff0c;合并到一处浏览”把自己关注的网页版块从不同网站上裁剪下来放在一个页面里&#xff0c;一次刷新就可以看到不同网站的最新内容&#xff0c;而不用逐个打开网站去看&#xff0c;提高了上网的效率。关键特征汇聚浏览…

排序算法(带动图)

0、算法概述0.1 算法分类十种常见排序算法可以分为两大类&#xff1a;比较类排序&#xff1a;通过比较来决定元素间的相对次序&#xff0c;由于其时间复杂度不能突破O(nlogn)&#xff0c;因此也称为非线性时间比较类排序。非比较类排序&#xff1a;不通过比较来决定元素间的相对…

【数据结构初阶】第七篇——二叉树的顺序结构及实现(堆的向下,向上调整算法)

二叉树的顺序结构 堆的概念及结构 堆的向下调整算法 堆的向上调整算法 堆的实现 初始化堆 销毁堆 打印堆 堆的插入 堆的删除 获取堆顶的数据 获取堆的数据个数 堆的判空 建堆的时间复杂度 二叉树的顺序结构 普通二叉树是不适合用数组来存储的,因为可能会导致大量…

为nginx配置好看的错误提示页面

前言 nginx默认错误页面确实有些丑哈&#xff0c;leeader让我换一个样式 &#xff0c;我就来喽&#xff01; 为nginx配置好看的错误提示页面前言1 找异常页原始页2 win上替换3 再linux服务器上替换4 不生效解决办法样式显示不正确6 错误页源码1 找异常页 原始页 nginx默认错误…