springboot整合SpringSecurity并实现简单权限控制

news2024/11/29 20:52:14

目录

一、SpringSecurity介绍

        案例效果:

二、环境准备

        2.1 数据库

        2.2 项目准备

三、确保项目没问题后开始使用

3.1、Security的过滤链:

3.2、自定义用户名密码登录:

方式1:将用户名密码写在配置文件里

方式2:使用数据库中的用户名、密码进行登录:

        第一步:新建一个类CustomerUserDetails实现UserDetails接口

        第二步:新建CustomerUserDetailsServiceImpl来实现UserDetailService接口

        第三步:配置类中注入bean对象:

3.3、自定义登录:

        第一步:自定义登录页面

        第二步:定义一个登录接口

        第三步:放行登录接口、请求登录接口

        第四步:在Service层使用ProviderManager的authenticate()方法进行验证

        实现效果:

过程中的一些报错:

认证过程:

                


一、SpringSecurity介绍

  SpringSecurity顾名思义是spring的一个安全框架。拥有认证和授权两大核心功能。

        案例效果:

二、环境准备

        2.1 数据库

RBAC模型:基于角色的权限控制。通过角色关联用户,角色关联权限的方式间接赋予用户权限。

即一个用户属于多种角色、一个角色有多个权限

 主体(subject) 访问资源的时候、通常由分为两种:基于角色控制访问、基于权限控制访问;

故建立五张表:用户表、权限表、角色表、用户角色表、角色权限表;

准备数据:

        张三--->管理员、普通用户------>增删改查 

       李四---->普通用户----->查询

脚本参考文章末尾的传送门

        2.2 项目准备

       使用mybatis作为持久层、且为了简化开发使用了mybatis-plus、本文着种于集成SpringSecurity,对整合mybatis-plus感兴趣的小伙伴可前往传送门浏览:

springboot整合mybatis-plus

       2.3 项目信息             

jdk 17 

springboot 2.7.0         

maven 3.8.6      

mysql 8.0.30

 导入必要jar包:主要导入:boot-security的整合依赖,其他根据需要导入

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

        <!--数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--        MP 起步依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <!-- 模板引擎 -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.3</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.0</version>
            <!--   <scope>test</scope> -->
        </dependency>

        <!--security-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--thymeleaf模块引擎-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!--   test-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

配置文件:application.yml

spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher
server:
  port: 8080
---
spring:
  datasource:
    url: jdbc:mysql://localhost:3308/boot_security?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&AllowPublicKeyRetrieval=True
    username: root
    password: root
    
---
mybatis-plus:
  mapper-locations: mapper/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true

随后使用代码生成工具生成项目结构、随后调整xml文件位置、以及适当删减、随后测试下生成的代码是否可用。主要看有对应三个实体类就可。

    @Autowired
    private TbUserService userService;

    @Test
    public void test01(){
        userService.list();
    }

静态资源准备:

三、确保项目没问题后开始使用

导入security整合依赖后、启动项目访问任何接口,都会被直接被直接拦截,并转发到security提供的登录页面、也就是需要认证一下才能进入首页。默认的用户名是user、密码在控制台。

        

 认证通过后才会访问到目标页面

3.1、Security的过滤链:

目前使用的是Security给的默认用户名和生成的密码。 实际情况是使用tb_user获取真实的用户名和密码;在此之前先了解Security的过滤链;

List<Filter> filterList = context.getBean(DefaultSecurityFilterChain.class).getFilters();

SpringSecurity的过滤链:一共有16个过滤器链

 过滤器链的大概流程就是,用户请求过来、先检查用户名密码、没有错、则检查权限,若有对应权限、访问对应的接口、其中只要一步错,就给打回去;

3.2、自定义用户名密码登录:

方式1:将用户名密码写在配置文件里

spring:
  security:
    user:
      name: zs
      password: 123

方式2:使用数据库中的用户名、密码进行登录:

        第一步:新建一个类CustomerUserDetails实现UserDetails接口

实现所有UserDetails的抽象方法并将TbUser【登录对象】 作为CustomerUserDetails的属性。

        

@Data
@NoArgsConstructor
@AllArgsConstructor
public class CustomerUserDetails implements UserDetails {

    private TbUser user;

    

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

   @Override
    public String getPassword() {
        return user.getPassWord();
    }

    @Override
    public String getUsername() {
        return user.getUserName();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

        第二步:新建CustomerUserDetailsServiceImpl来实现UserDetailService接口

        实现loadUserByUsername方法。

/**
 * @author Alex
 */
@Service
public class CustomerUserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private TbUserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名查询用户信息
        LambdaQueryWrapper<TbUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TbUser::getUserName,username);
        TbUser user = userMapper.selectOne(wrapper);
        //如果查询不到数据就通过抛出异常来给出提示
        if(Objects.isNull(user)){
            throw new RuntimeException("用户名错误");
        }

        //封装成UserDetails对象返回
        return new CustomerUserDetails(user);
    }
}

此时由于数据库中的密码是明文,登录时会报一个错。

There is no PasswordEncoder mapped for the id "null"

因为没有给密码加密:

此时要想继续登录

                方式1【不推荐】:将数据库中明文前加{noop}即可

                方式2:使用Security默认的加密的工具类BCryptPasswordEncoder将密码加密后存入数据库。再SecurityConfig配置类中注入BCryptPasswordEncoder的bean对象即可。加密方式会自动加盐;

        第三步:配置类中注入bean对象:

/**
 * @author Alex
 */
@Configuration
public class SecurityConfig{

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

}

    将密码字符串加密,调用encode()将密码加密。将加密后的字符串存入数据库;

    @Test
    public void testPasswordEncoder1(){

        String encode = securityConfig.passwordEncoder().encode("123");
        System.err.println(encode);

    }


    @Test
    public void testPasswordEncoder(){
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encode = passwordEncoder.encode("123");
        System.err.println(encode);
    }

         当注入bean对象后,明文前加{noop}就不可用了。

     简单提一下解密:

SpringSecurity提供了matches()方法来进行密码匹配,加密本身时不可逆的,解密的原理是将需要解密的字段统过相同的Hash函数得到的字符串到已加密的数据库中进行匹配。

3.3、自定义登录/认证:

        第一步:自定义登录页面

        第二步:定义一个登录接口

    /**
     * 登录方法、登录成功跳转到首先、
     * 否则继续跳转登录页,并给出提示
     * @param username
     * @return
     */
    @PostMapping("/login")
    public Map<String, String> userLogin(String username, String password){
        TbUser loginUser = new TbUser();
        loginUser.setUserName(username);
        loginUser.setPassWord(password);
        return userService.userLogin(loginUser);
    }

        第三步:放行登录接口、请求登录接口

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                //关闭csrf
                .csrf().disable()
                .authorizeRequests()
                // 允许匿名访问的接口
                .antMatchers("/user/login").anonymous()
                .antMatchers("/toLogin").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated();

        http.formLogin()
                // 访问登录页面接口
                .loginPage("/toLogin")
                // 执行登录方法接口
                .loginProcessingUrl("user/login");

        return http.build();
    }

        第四步:在Service层使用ProviderManager的authenticate()方法进行验证

将封装的Authentication对象 存入SecurityContextHolder

    @Override
    public Map<String, String> userLogin(TbUser loginUser, HttpSession session) {
        Map<String, String> responseMap = new HashMap<>(2);
        try {
            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(loginUser.getUserName(), loginUser.getPassWord(),null);
            Authentication authenticate = authenticationManager.authenticate(token);
            //   存入SecurityContextHolder
            SecurityContextHolder.getContext().setAuthentication(authenticate);
            responseMap.put("code","0");
            // 存入部分数据到session,方便区分用户、实际情况种可以省略
            CustomerUserDetails userDetails = (CustomerUserDetails) authenticate.getPrincipal();
            session.setAttribute("userName",userDetails.getUser().getUserName());
            return responseMap;
        }catch (RuntimeException e){
            responseMap.put("code","-1");
            e.printStackTrace();
            return responseMap;
        }
    }

此处直接返回map,也可以封装一个返回结果集对象,然后对于Security的 Session Management相关的内容会在后续文章中更新。

 前端监听表单提交后发送登录请求:

        //登录请求
            const url = "user/login";
            $.post(url,data,function(response){
                console.log(response.code);
                if(response.code==0){
                    layer.msg("登录成功",{icon:6,time:1000}, function () {
                            window.location = '/';
                        });
                }else {
                    layer.msg("用户名或密码错误",{icon:5,anim:6});
                    $("#btn-login").removeAttr("disabled", "disabled").removeClass("layui-btn-disabled");
                }
            });

到这里呢、自定义登录就完成了、看下登录后的跳转的首页

        实现效果:

3.3、后端接口授权

        

3.4、前端按钮隐藏

过程中的一些报错

1、Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.

解决:配置文件加上

debug: true

2、No qualifying bean of type 'com.example.demo.mapper.TbUserMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

解决:启动类上加上mapperScan("com......")

@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}

认证过程:

问:springSecurity如何校验用户名、密码和权限?

答:通过一个登录请求的debug得到如下流程
        发送一个登录请求----->UsernamePasswordAuthenticationFilter--->通过authenticate()方法认证----->loadUserUsername()方法获得UserDetails对象-->从该对象中拿到密码对比系统中的密码---->给UserDetails对象添加权限并设置到Authentication中,存入SecurityContentHolder中即可。  (完成一个cookie--session的闭环)

                

  传送门:

springboot整合thymeleaf

springboot整合mybatis

springboot整合mybatis-plus

springboot整合shiro实现简单权限控制

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

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

相关文章

刷题之莲子的软件工程学和机械动力学以及物理热力学

目录 1、莲子的软件工程学 1&#xff09;题目 2&#xff09;题目解析 3&#xff09;代码 2、莲子的机械动力学 2&#xff09;题目解析 3&#xff09;代码 3、莲子的物理热力学 1&#xff09;、题目 2&#xff09;题目解析 1、莲子的软件工程学 1&#xff09;题目 题目背景…

Linux下的进程控制-进程程序替换

这篇主要说一下Linux下的进程控制中最后一部分内容&#xff1a;进程程序替换。 文章目录1. 进程程序替换1.1 为什么要进程程序替换1.2 替换原理1.3 如何进行程序替换1.3.1 execl函数1.3.2 引入子进程的程序替换1.3.3 execv函数1.3.4 execlp函数和execvp函数1.3.5 如何执行其它…

Flutter自定义对话框返回相关问题汇总

Flutter自定义对话框&#xff0c;禁用系统返回按钮 - WillPopScope 使用WillPopScope即可&#xff0c;重点onWillPop方法: Future<bool> _onWillPop()>new Future.value(false); 由于要弹出dialog&#xff0c;我这里是禁掉返回按钮&#xff0c;当然也可以在这里做一下…

基于SpringBoot的二手商品交易平台

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SpringBoot 前端&#xff1a;Vue、HTML 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#…

3.6.4、随机接入-CSMA/CA协议

无线局域网使用的协议 1、基本概念 对于上述无线局域网为什么 A 和 C 都检测不到对方的无线信号 因为 C 不在 A 的范围内&#xff0c;C 发送无线信号会导致 A 检测不到 C 在发送 对于上述使用广播信道的有线局域网就不会存在这样的问题 总线上某个主机发送的信号&#xff0…

JVM虚拟机字节码执行引擎——类文件和类加载之前必看

文章目录虚拟机字节码执行引擎运行时栈帧结构局部变量表&#xff08;Local Variables&#xff09;操作数栈动态链接&#xff08;Dynamic Linking&#xff09;方法返回地址附加信息方法调用解析分派虚方法和非虚方法普通调用指令&#xff1a;动态调用指令&#xff1a;动态类型语…

SpringBoot SpringBoot 原理篇 2 自定义starter 2.7 开启yml 提示功能

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 原理篇 文章目录SpringBootSpringBoot 原理篇2 自定义starter2.7 开启yml 提示功能2.7.1 问题引入2.7.2 开启yml提示功能2.…

【矩阵论】正规方程——求解

5.2 正规方程 AHAxAHb为Axb的正规方程\begin{aligned} A^HAxA^Hb为Axb的正规方程 \end{aligned} AHAxAHb为Axb的正规方程​ 5.2.1 正规方程必有解 正规方程 AHAxAHbA^HAxA^HbAHAxAHb 必有解 &#xff0c;且特解为 x0Abx_0A^bx0​Ab &#xff0c;使 AHAx0AHbA^HAx_0A^HbAHAx0​…

一文熟悉 Go 的循环结构 —— for 循环

哈喽大家好&#xff0c;我是陈明勇&#xff0c;今天分享的知识是 Go 的循环结构。如果本文对你有帮助&#xff0c;不妨点个赞&#xff0c;如果你是 Go 语言初学者&#xff0c;不妨点个关注&#xff0c;一起成长一起进步&#xff0c;如果本文有错误的地方&#xff0c;欢迎指出&a…

【Spring】——10、@PostConstruct注解和@PreDestroy注解

&#x1f4eb;作者简介&#xff1a;zhz小白 公众号&#xff1a;小白的Java进阶之路 专业技能&#xff1a; 1、Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理 2、熟悉Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理&#xff0c;具备⼀定的线…

OpenStack集群部署——Keystone部署(二)

三、Keyston-认证服务 3.1 Keyston介绍 Keyston介绍 补充 3.2 安装时间同步器 ----------------------------------------------------使用chrony安装------------------------------------------------- ####所有节点 #下载安装chrony yum -y install chrony #修改配置…

【心电信号】Simulink胎儿心电信号提取【含Matlab源码 1550期】

⛄一、心电信号简介 0 引言 心电信号是人类最早研究的生物信号之一, 相比其他生物信号更易于检测, 且具有直观的规律。心电图的准确分析对心脏病的及早治疗有重大的意义。人体是一个复杂精密的系统, 有许多不可抗的外界因素, 得到纯净的心电信号非常困难。可以采用神经网络算法…

MongoDB 分片集群

之前说到了主从集群&#xff0c;关于主从集群的搭建以及细节后面会再次分享&#xff0c;这次我们先初步来看看 分片集群 举个例子 例如我们有几百G甚至更多的数据&#xff0c;可是我们只有单个副本集&#xff0c;数据量这么大&#xff0c;网络 IO &#xff0c;CPU &#xff0c…

《深度学习的数学》chap1 神经网络的思想

《深度学习的数学》chap1 神经网络的思想 文章目录1-1 神经网络和深度学习神经网络用神经网络实现的人工智能“人教导机器”类型的人工智能的问题1-2 神经元工作的数学表示整理神经元的工作神经元工作的数学表示点火条件的图形表示1-3 激活函数&#xff1a;将神经元的工作一般化…

开源项目-排班管理系统,考勤管理系统

哈喽&#xff0c;大家好&#xff0c;今天给大家带来一个开源系统-排版管理系统 ​​​​​​​git上搜索可以FinalScheduler-master可以了解详情 也可以通过csdn下载​​​​​​​ 该系统主要用于人员的排班使用&#xff0c;主要用人员管理&#xff0c;排班管理&#xff0c…

Java-ForkJoinPool(线程池-工作窃取算法)

文章目录概述工作窃取算法工作窃取算法的优缺点使用 ForkJoinPool 进行分叉和合并ForkJoinPool使用RecursiveActionRecursiveTaskFork/Join 案例Demo概述 Fork 就是把一个大任务切分为若干个子任务并行地执行&#xff0c;Join 就是合并这些子任务的执行结果&#xff0c;最后得到…

《精神与爱欲》爱源于母性,且超越性别

《精神与爱欲》爱源于母性&#xff0c;且超越性别 赫尔曼黑塞&#xff08;1877-1962&#xff09;&#xff0c;作家&#xff0c;诗人&#xff0c;画家。1877年生于德国&#xff0c;1924年入籍瑞士。1946年获诺贝尔文学奖。被誉为“德国浪漫派的最后一位骑士”。 文章目录《精神与…

扩展函数和运算符重载

扩展函数和运算符重载 扩展函数 扩展函数表示在不改变某个类的源代码的情况下,仍然可以打开这个类,向该类中添加新的函数为了能够更好的理解扩展函数的功能,先来思考一个问题:给定一个字符串,这个字符串由字母,数字,特殊符号组成,我们想要统计这个字符串当中字母的个数可以这…

第十章 开源许可证

软件是一种著作&#xff0c;天然是拥有版权的。很多人会认为放在 Github 上的就是开源软件&#xff0c;既然放了源代码&#xff0c;我就可以随便使用了。其实版权法规定著作是禁止共享的&#xff0c;也就是说没有许可证的软件等于保留版权。虽然源代码公开了&#xff0c;但并不…

GUI编程--PyQt5--QLabel

文章目录QLabel 文本展示QLabel 图片展示QLCDNumberQProgressBarQErrorMessageQProgressDialogQLabel 文本展示 展示文本、富文本、图片、动画。 # 实例化 label QLabel(self) # 设置文本 label.setText("666") # 设置图片 label.setPixmap(QPixmap) label.resize…