Spring Security实现基于数据库实现认证

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 本文档介绍了如何在Spring Security框架中基于数据库实现用户认证。首先,Spring Security提供了一个`UserDetailsService`接口,用于获取用户详细信息,通常在用户尝试登录时被调用。

基于数据库实现认证

我们刚才的实现都是基于内存来构建用户的,在实际开发中,用户肯定会保存到数据库中,在Spring Security框架中提供了一个UserDetailsService 接口

它的主要作用是提供用户详细信息。具体来说,当用户尝试进行身份验证时,UserDetailsService 会被调用,以获取与用户相关的详细信息。这些详细信息包括用户的用户名、密码、角色等

1. 执行流程

暂时无法在飞书文档外展示此内容

新创建一个UserDetailsServiceImpl,让它实现UserDetailsService ,代码如下

@Component
public class UserDetailsServiceImpl  implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询数据库中的用户,并且返回框架要求的UserDetails 
        return null;
    }
}
  • 当前对象需要让spring容器管理,所以在类上添加注解@Component
  • 大家注意一下loadUserByUsername方法的返回值,叫做UserDetails,这也是框架给提供了保存用户的类,并且也是一个接口,如果我们有自定义的用户信息存储,可以实现这个接口,我们后边会详细讲解

既然以上能使用这个类来查询用户信息,那么我们之前在SecurityConfig中定义的用户信息,可以注释掉了,如下:

/*
    @Bean
    public UserDetailsService users() {
        UserDetails user = User.builder()
                .username("user")
                .password("$2a$10$2VCyByZ5oeiXCEN73wvBB.xpmJgPBbZVS/Aallmdyij2G7hmAKQTG")
                .roles("USER")
                .build();
        UserDetails admin = User.builder()
                .username("admin")
                .password("$2a$10$cRH8iMMh6XO0T.ssZ/8qVOo8ThWs/qfntIH3a7yfpbPd05h9ZGx8y")
                .roles("USER", "ADMIN")
                .build();
        return new InMemoryUserDetailsManager(user, admin);
    }*/
  1. 数据库查询用户

我们下面就来实现数据库去查询用户,我们可以直接使用我们项目中的用户表,实现的步骤如下:

  • 导入相关依赖(数据库、mybaits、lombok等)
  • 添加配置:连接数据库、mybatis配置等(application.yml)
  • 编写实体类和mapper
  • 改造UserDetailsServiceImpl(用户从数据库中获取)


1. pom文件添加依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.1</version>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>
<!--MySQL支持-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.19</version>
</dependency>

2. application.yml添加数据库相关配置

#服务配置
server:
  #端口
  port: 8080
spring:
  application:
    name: springsecurity-demo
  #数据源配置
  datasource:
    druid:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://192.168.200.146:3306/security_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
      username: root
      password: root
# MyBatis配置
mybatis:
  #mapper配置文件
  mapper-locations: classpath*:mapper*/*Mapper.xml
  type-aliases-package: com.itheima.project.entity
  configuration:
    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 驼峰下划线转换
    map-underscore-to-camel-case: true
    use-generated-keys: true
    default-statement-timeout: 60
    default-fetch-size: 100

3. 表结构及实体类和mapper

新创建一个数据库,名字为:security_db

导入当天资料的sql脚本


用户实体类

package com.itheima.project.entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class User {
    public Long id;
    /**
     * 用户账号
     */
    private String username;
    /**
     * 密码
     */
    private String password;
    /**
     * 用户昵称
     */
    private String nickName;
}

用户mapper,我们只需要定义一个根据用户名查询的方法即可

package com.itheima.project.mapper;
import com.itheima.project.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
/**
 * @author sjqn
 */
@Mapper
public interface UserMapper {
    @Select("select * from sys_user where username = #{username}")
    public User findByUsername(String username);
}

4. 改造UserDetailsServiceImpl

package com.zzyl.security.service;
import com.zzyl.security.entity.User;
import com.zzyl.security.entity.UserAuth;
import com.zzyl.security.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.SimpleTimeZone;
/**
 * @author sjqn
 */
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询用户
        User user = userMapper.findByUsername(username);
        if(user == null){
            throw new RuntimeException("用户不存在或已被禁用");
        }
        SimpleGrantedAuthority user_role = new SimpleGrantedAuthority("user");
        SimpleGrantedAuthority admin_role = new SimpleGrantedAuthority("admin");
        List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
        list.add(user_role);
        list.add(admin_role);
        return new org.springframework.security.core.userdetails.User(user.getUsername()
                ,user.getPassword()
                , list);
    }
}

测试

重启项目之后,可以根据数据库中有的用户进行登录,如果登录成功则表示整合成功


2、自定义UserDetails

上述代码中,返回的UserDetails或者是User都是框架提供的类,我们在项目开发的过程中,很多需求都是我们自定义的属性,我们需要扩展该怎么办?

其实,我们可以自定义一个类,来实现UserDetails,在自己定义的类中,就可以扩展自己想要的内容,如下代码:

package com.zzyl.security.entity;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
 * @author sjqn
 * @date 2023/9/1
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserAuth implements UserDetails {
    private String username; //固定不可更改
    private String password;//固定不可更改
    private String nickName;  //扩展属性  昵称
    private List<String> roles; //角色列表
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        if(roles==null) return null;
        //把角色类型转换并放入对应的集合
        return roles.stream().map(role -> new SimpleGrantedAuthority("ROLE_"+role)).collect(Collectors.toList());
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return true;
    }
}

然后,我们可以继续改造UserDetailsServiceImpl中检验用户的逻辑,代码如下:

package com.itheima.project.service;
import com.itheima.project.entity.User;
import com.itheima.project.mapper.UserMapper;
import com.itheima.project.vo.UserAuth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
 * @author sjqn
 */
@Component
public class UserDetailServiceImpl implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询用户
        User user = userMapper.findByUsername(username);
        if(user == null){
            throw new RuntimeException("用户不存在或已被禁用");
        }
        UserAuth userAuth = new UserAuth();
        userAuth.setUsername(user.getUsername());
        userAuth.setPassword(user.getPassword());
        userAuth.setNickName(user.getNickName());
        //添加角色
        List<String> roles=new ArrayList<>();
        //加入判断,方便后面授权的时候使用
        if("user@qq.com".equals(username)){
            roles.add("USER");
            userAuth.setRoles(roles);
        }
        if("admin@qq.com".equals(username)){
            roles.add("USER");
            roles.add("ADMIN");
            userAuth.setRoles(roles);
        }
        return userAuth;
    }
}

修改HelloController,使用getPrincipal()方法读取认证主体对象。

/**
 * @ClassName
 * @Description
 */
@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String hello(){
      //认证成功,可以得到用户信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String name = authentication.getName();
        //获取自定义属性
        UserAuth principal = (UserAuth) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        return "hello"+name+"   昵称:"+principal.getNickName();
    }
}

测试

重启项目之后,可以根据数据库中有的用户进行登录,如果登录成功则表示整合成功


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
6天前
|
XML 安全 前端开发
Spring Security 重点解析(下)
Spring Security 重点解析
18 1
|
6天前
|
安全 Java 数据安全/隐私保护
|
6天前
|
安全 Java API
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)
52 0
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
|
6天前
|
存储 安全 前端开发
第6章 Spring Security 的 Web 安全性(2024 最新版)(上)
第6章 Spring Security 的 Web 安全性(2024 最新版)
58 0
|
6天前
|
安全 Java Go
第6章 Spring Security 的 Web 安全性(2024 最新版)(下)
第6章 Spring Security 的 Web 安全性(2024 最新版)
64 1
|
6天前
|
安全 NoSQL Java
Spring Security 重点解析(上)
Spring Security 重点解析
20 1
|
6天前
|
Cloud Native 关系型数据库 分布式数据库
数据库性能诊断工具DBdoctor通过阿里云PolarDB产品生态集成认证
DBdoctor(V3.1.0)成功通过阿里云PolarDB分布式版(V2.3)集成认证,展现优秀兼容性和稳定性。此工具是聚好看科技的内核级数据库性能诊断产品,运用eBPF技术诊断SQL执行,提供智能巡检、根因分析和优化建议。最新版V3.1.1增加了对PolarDB-X和OceanBase的支持,以及基于cost的索引诊断功能。PolarDB-X是阿里巴巴的高性能云原生分布式数据库,兼容MySQL生态。用户可通过提供的下载地址、在线试用链接和部署指南体验DBdoctor。
155 0
|
6天前
|
安全 Java 数据库连接
在IntelliJ IDEA中通过Spring Boot集成达梦数据库:从入门到精通
在IntelliJ IDEA中通过Spring Boot集成达梦数据库:从入门到精通
|
6天前
|
存储 安全 Java
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)(下)
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)
26 2
|
6天前
|
安全 Cloud Native Java
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)(上)
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)
32 2