当前位置: 首页 > news >正文

07 - spring security基于数据库的账号密码

spring security基于数据库的账号密码

文档

  1. 00 - spring security框架使用
  2. 01 - spring security自定义登录页面
  3. 02 - spring security基于配置文件及内存的账号密码
  4. 03 - spring security自定义登出页面
  5. 04 - spring security关闭csrf攻击防御
  6. 05 - spring security权限控制
  7. 06 - spring security角色和权限设置

基于数据库的账号密码

调整配置
  1. pom.xml文件添加依赖,这里使用mybatis-plus

    <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope>
    </dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.12</version>
    </dependency>
    
  2. 调整applicaiton.yml

    server:port: 8080servlet:context-path: /springsecurity03spring:security:user:name: adminpassword: 123456datasource:url: jdbc:mysql://127.0.0.1:3306/important?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8username: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driverjackson:time-zone: GMT+8mybatis-plus:mapper-locations: classpath:mybatis/mapper/*.xmlconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
  3. 添加实体类SpringSecurityUser.class

    package xin.yangshuai.springsecurity03.entity;import com.baomidou.mybatisplus.annotation.TableName;import java.io.Serializable;@TableName("`spring_security_user`")
    public class SpringSecurityUser implements Serializable {/*** 主键*/private Integer id;/*** 用户名*/private String username;/*** 密码*/private String password;private static final long serialVersionUID = 1L;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username == null ? null : username.trim();}public String getPassword() {return password;}public void setPassword(String password) {this.password = password == null ? null : password.trim();}
    }
    
  4. 创建mapperSpringSecurityUserMapper.java

    package xin.yangshuai.springsecurity03.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import xin.yangshuai.springsecurity03.entity.SpringSecurityUser;public interface SpringSecurityUserMapper extends BaseMapper<SpringSecurityUser> {
    }
    
  5. 启动类添加mybatis-plus包扫描

    package xin.yangshuai.springsecurity03;import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
    @MapperScan(basePackages = {"xin.yangshuai.springsecurity03.mapper"})
    public class Springsecurity03Application {public static void main(String[] args) {SpringApplication.run(Springsecurity03Application.class, args);}}
    
配置基于数据库的账号密码
  1. 在基于内存的账号密码时,我们定义了一个UserDetailsService类型的Bean

    @Configuration
    // @EnableWebSecurity
    @EnableMethodSecurity //开启基于方法的授权
    public class WebSecurityConfig {@Beanpublic UserDetailsService userDetailsService() {InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();// 此时配置文件中的用户名和密码将不可用manager.createUser(User.withDefaultPasswordEncoder().username("user").password("password").authorities("USER_LIST").build());return manager;}// ...
    }
    
  2. spring security框架在认证的时候就会调用该类的loadUserByUsername方法

  3. 使用基于数据库的账号密码时,需要要将上面的Bean注释掉

  4. 自定义一个类DatabaseUserDetailsManager.java,实现UserDetailsManager接口,并接受Spring管理,同样的,spring security框架在认证的时候就会调用该类的loadUserByUsername方法

    package xin.yangshuai.springsecurity03.config;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.provisioning.UserDetailsManager;
    import org.springframework.stereotype.Component;
    import xin.yangshuai.springsecurity03.entity.SpringSecurityUser;
    import xin.yangshuai.springsecurity03.mapper.SpringSecurityUserMapper;@Component
    public class DatabaseUserDetailsManager implements UserDetailsManager {@Autowiredprivate SpringSecurityUserMapper springSecurityUserMapper;@Overridepublic void createUser(UserDetails user) {}@Overridepublic void updateUser(UserDetails user) {}@Overridepublic void deleteUser(String username) {}@Overridepublic void changePassword(String oldPassword, String newPassword) {}@Overridepublic boolean userExists(String username) {return false;}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {SpringSecurityUser springSecurityUser1 = springSecurityUserMapper.selectById("123");System.out.println(springSecurityUser1);LambdaQueryWrapper<SpringSecurityUser> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(SpringSecurityUser::getUsername, username);SpringSecurityUser springSecurityUser = springSecurityUserMapper.selectOne(queryWrapper);if (springSecurityUser == null) {throw new UsernameNotFoundException(username);} else {UserDetails user = User.builder().username(springSecurityUser.getUsername()).password(springSecurityUser.getPassword())// 这里示例,权限手动添加.authorities("USER_ADD", "USER_UPDATE", "USER_LIST").build();return user;}}
    }
    
添加用户
  1. 创建serviceSpringSecurityUserService.java

    package xin.yangshuai.springsecurity03.service;import xin.yangshuai.springsecurity03.entity.SpringSecurityUser;import java.util.List;public interface SpringSecurityUserService {List<SpringSecurityUser> list();int add(SpringSecurityUser springSecurityUser);
    }
    
  2. 创建service实现类SpringSecurityUserServiceImpl.java

    package xin.yangshuai.springsecurity03.service.impl;import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
    import org.springframework.security.crypto.password.MessageDigestPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.stereotype.Service;
    import xin.yangshuai.springsecurity03.entity.SpringSecurityUser;
    import xin.yangshuai.springsecurity03.mapper.SpringSecurityUserMapper;
    import xin.yangshuai.springsecurity03.service.SpringSecurityUserService;import java.util.HashMap;
    import java.util.List;
    import java.util.Map;@Service
    public class SpringSecurityUserServiceImpl implements SpringSecurityUserService {@Autowiredprivate SpringSecurityUserMapper springSecurityUserMapper;@Overridepublic List<SpringSecurityUser> list() {return springSecurityUserMapper.selectList(null);}@Overridepublic int add(SpringSecurityUser springSecurityUser) {if (springSecurityUser == null) {return 0;}// org.springframework.security.crypto.factory.PasswordEncoderFactoriesString encodingId = "bcrypt";Map<String, PasswordEncoder> encoders = new HashMap();encoders.put(encodingId, new BCryptPasswordEncoder());encoders.put("MD5", new MessageDigestPasswordEncoder("MD5"));// ...encodingId = "MD5";PasswordEncoder encoder = new DelegatingPasswordEncoder(encodingId, encoders);String password = springSecurityUser.getPassword();String encode = encoder.encode(password);springSecurityUser.setPassword(encode);return springSecurityUserMapper.insert(springSecurityUser);}
    }
    
  3. 修改SpringSecurityUserController.java

    package xin.yangshuai.springsecurity03.controller;import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.access.prepost.PreAuthorize;
    import org.springframework.web.bind.annotation.*;
    import xin.yangshuai.common01.entity.BaseResult;
    import xin.yangshuai.springsecurity03.entity.SpringSecurityUser;
    import xin.yangshuai.springsecurity03.service.SpringSecurityUserService;import java.util.List;@RestController
    @RequestMapping("user")
    public class SpringSecurityUserController {@Autowiredprivate SpringSecurityUserService springSecurityUserService;@GetMapping("list")@PreAuthorize("hasAuthority('USER_LIST')")public BaseResult<List<SpringSecurityUser>> list() {List<SpringSecurityUser> list = springSecurityUserService.list();BaseResult<List<SpringSecurityUser>> result = new BaseResult<>();result.setCode("200");result.setData(list);return result;}@PostMapping("add")public BaseResult<Integer> add(@RequestBody SpringSecurityUser springSecurityUser) {int rows = springSecurityUserService.add(springSecurityUser);BaseResult<Integer> result = new BaseResult<>();result.setCode("200");result.setData(rows);return result;}
    }
    
  4. 为避免麻烦,关闭csrf攻击防御,对user/add接口设置免认证,编辑WebSecurityConfig.java

    package xin.yangshuai.springsecurity03.config;import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.Customizer;
    import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configurers.*;
    import org.springframework.security.web.SecurityFilterChain;@Configuration
    // @EnableWebSecurity
    @EnableMethodSecurity //开启基于方法的授权
    public class WebSecurityConfig {//    @Bean
    //    public UserDetailsService userDetailsService() {
    //        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
    //        // 此时配置文件中的用户名和密码将不可用
    //        manager.createUser(User.withDefaultPasswordEncoder().username("user").password("password").authorities("USER_LIST").build());
    //        return manager;
    //    }@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {// 开启授权保护http.authorizeRequests(new Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry>() {@Overridepublic void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry expressionInterceptUrlRegistry) {// 具有USER_LIST权限的用户可以访问/user/list// expressionInterceptUrlRegistry.requestMatchers("/user/list").hasAuthority("USER_LIST");expressionInterceptUrlRegistry.requestMatchers("/user/add").permitAll()// 对所有请求开启授权保护.anyRequest()// 已经认证的请求会被自动授权.authenticated();}});// 自定义登录页面// 自定义登出页面// 自定义拒绝访问页面// 关闭csrf攻击防御http.csrf(new Customizer<CsrfConfigurer<HttpSecurity>>() {@Overridepublic void customize(CsrfConfigurer<HttpSecurity> httpSecurityCsrfConfigurer) {httpSecurityCsrfConfigurer.disable();}});return http.build();}
    }
    
  5. 这里有个细节,spring security进行账号认证的时候,系统内的用户信息,也就是UserDetails对象的密码需要传入经过某种算法加密后的值,比如:bcrypt算法,具体参考org.springframework.security.crypto.password.DelegatingPasswordEncoder#matches

  6. org.springframework.security.crypto.factory.PasswordEncoderFactories类中,列举了加密算法

  7. 在使用基于内存的账号密码时,构造UserDetails对象时,使用的.withDefaultPasswordEncoder()方法,会将密码进行加密,使用的算法就是bcrypt

  8. 在使用基于数据库的账号密码时,构造UserDetails对象时,并没有使用.withDefaultPasswordEncoder()方法,使用的是.builder()方法,这个方法并不会对原始密码加密

  9. 基于以上,可以将加密后的密码保存在数据库里,这样也能避免数据库中存储用户的原始密码

http://www.dtcms.com/a/348686.html

相关文章:

  • window11无法连接Fortinet SSL VPN
  • Elasticsearch如何确保数据一致性?
  • 『深度编码』操作系统-进程之间的通信方法
  • 记录一下TVT投稿过程
  • 阿里云大模型应用实战:从技术落地到业务提效
  • Dify 从入门到精通(第 53/100 篇):Dify 的分布式架构(进阶篇)
  • 兑换汽水瓶
  • 关于并查集
  • 数字营销岗位需要具备的能力有哪
  • Java 内存模型(JMM)与并发可见性:深入理解多线程编程的基石
  • Flink session cluster与Flink per-job cluster区别
  • Zynq开发实践(Verilog、仿真、FPGA和芯片设计)
  • Linux-函数的使用-编写监控脚本
  • 栈的创建和基本操作
  • Arbess V1.1.4版本发布,支持Mysql数据库,Ubuntu系统,新增SSH及Hadess上传下载任务
  • week4-[字符数组]月份
  • TCP连接与UDP协议
  • 构建现代前端工程:Webpack/Vite/Rollup配置解析与最佳实践
  • C++20: std::span
  • 目标检测数据集 第005期-基于yolo标注格式的PCB组件检测数据集(含免费分享)
  • 【Ollama】本地OCR
  • 基于SpringBoot的校园信息共享系统【2026最新】
  • pod管理
  • scanner、arrylist、反转数组
  • FPGA 时序分析(五)
  • 十、redis 入门 之 redis事务
  • (Redis)主从哨兵模式与集群模式
  • 【机器学习】7 Linear regression
  • VScode设置鼠标滚轮调节代码
  • 嵌入式第三十六天(网络编程(TCP))