Spring Security6 数据库认证

本章节将介绍如何通过 Spring Security + 数据库(MySQL)实现权限登陆认证,授权将在后续章节介绍。

数据库表设计

下面将使用 MySQL 数据库,分别创建用户(users)、角色(role)、菜单(menu)、角色用户(role_user)、角色菜单(role_menu)数据库表,如下图:

Spring Security 数据库认证

首先创建数据库 spring_security,然后执行如下 SQL 脚本:

-- 用户信息表
create table users(
  id bigint primary key auto_increment,
  username varchar(20) unique not null,
  password varchar(100)
);
-- 密码:123456
insert into users values(1,'zhangsan','$2a$10$5wJSJGpCj1dFRKTWjdTyJ.NI2CTwFsBIyf.o8A07sH.MtqMFAbmWi');
-- 密码:aaaaaa
insert into users values(2,'lisi','$2a$10$BaNPfmY3euVmVlI/E2V7CedEYHyPuBINMVALTNKKQI6KxILhUOB3W');

-- 角色表
create table role(
  id bigint primary key auto_increment,
  name varchar(20)
);
insert into role values(1,'管理员');
insert into role values(2,'普通用户');

-- 角色用户表
create table role_user(
  uid bigint,
  rid bigint
);
insert into role_user values(1,1);
insert into role_user values(2,2);

-- 菜单表
create table menu(
  id bigint primary key auto_increment,
  name varchar(20),
  url varchar(100),
  parentid bigint,
  permission varchar(20)

);
insert into menu values(1,'系统管理','',0,'menu:sys');
insert into menu values(2,'用户管理','',0,'menu:user');

-- 角色菜单表
create table role_menu(
  mid bigint,
  rid bigint
);
insert into role_menu values(1,1);
insert into role_menu values(2,1);
insert into role_menu values(2,2);

执行完上述 SQL 脚本后,数据库表如下:

Spring Security 数据库认证

其中,users 数据表的数据如下,存在两个用户:

Spring Security 数据库认证

项目结构

为了演示使用 Spring Security 结合数据库实现登录功能,创建 spring_security_demo3 演示项目,项目结构如下图:

Spring Security 数据库认证

后续将逐一介绍该演示项目各个文件的作用和源码。

pom.xml 依赖

完整的 maven 依赖请参考“Spring Security 入门示例”,下面是新增的 mybatis 和 mysql 依赖:

<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>3.0.4</version>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.14</version>
</dependency>

application.properties 配置

在 application.properties 配置文件中新增数据源配置信息,如下:

spring.application.name=spring_security_demo3

# 数据库配置信息
# 数据库驱动
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据库JDBC地址
spring.datasource.url=jdbc:mysql://localhost:3306/spring_security?serverTimezone=Asia/Shanghai&useSSL=false&characterEncoding=UTF-8
# 用户名
spring.datasource.username=root
# 密码
spring.datasource.password=aaaaaa

如果采用 YAML 格式,配置类似。

User.java 实体

编写一个 User 实体,用于建立与 users 表的映射,如下:

package com.hxstrive.spring_security.model;

import java.io.Serializable;

public class User implements Serializable {
    /** 用户ID */
    private Integer id;
    /** 用户名 */
    private String username;
    /** 密码 */
    private String password;

    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;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

UserMapper.java

定义一个 MyBatis Mapper,实现根据用户名查询用户信息,如下:

package com.hxstrive.spring_security.mapper;

import com.hxstrive.spring_security.model.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

/**
 * 用户Mapper
 * @author hxstrive.com
 */
@Mapper
public interface UserMapper {
    /**
     * 根据用户名查询用户信息
     * @param username 用户名
     * @return 返回用户信息
     */
    @Select({"select * from users where username = #{username}"})
    User selectUserByUserName(@Param("username") String username);
}

UserService.java 服务接口

定义一个服务,该服务用于根据用户名查询用户信息:

package com.hxstrive.spring_security.service;


import com.hxstrive.spring_security.model.User;

/**
 * 用户服务接口
 * @author hxstrive.com
 */
public interface UserService {
    /**
     * 根据用户名查询用户信息
     * @param username 用户名
     * @return 用户信息
     */
    User getUserByName(String username);
}

UserServiceImpl.java 服务实现

通过调用 UserMapper 来实现 UserService 服务接口的 getUserByName() 方法查询用户,如下:

package com.hxstrive.spring_security.service.impl;

import com.hxstrive.spring_security.mapper.UserMapper;
import com.hxstrive.spring_security.model.User;
import com.hxstrive.spring_security.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 用户服务实现
 * @author hxstrive.com
 */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public User getUserByName(String username) {
        return userMapper.selectUserByUserName(username);
    }
}

CustomUserDetailsService.java 重点

编写自定义的 UserDetailsService 实现类,该类将使用 UserService 服务从数据库中查询用户信息,如下:

package com.hxstrive.spring_security.config;

import com.hxstrive.spring_security.service.UserService;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

/**
 * 自定义实现 UserDetailsService 接口
 * @author hxstrive.com
 */
public class CustomUserDetailsService implements UserDetailsService {
    // 用户服务
    private UserService userService;

    public CustomUserDetailsService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("loadUserByUsername(String username) :: " + username);

        // 从数据库中加载用户信息
        com.hxstrive.spring_security.model.User user = userService.getUserByName(username);
        if(null == user){
            // 用户名不存在。抛出异常
            throw new UsernameNotFoundException("用户名 [" + username + "] 不存在");
        }

        // 创建 UserDetails 对象
        return new User(username, user.getPassword(), AuthorityUtils.createAuthorityList());
    }

}

SecurityConfig.java 配置类

添加配置类,将自定义的 CustomUserDetailsService 和 BCryptPasswordEncoder 注入到 Spring Security 中,如下:

package com.hxstrive.spring_security.config;

import com.hxstrive.spring_security.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;

/**
 * Spring Security 配置类
 * @author hxstrive.com
 */
@Configuration
public class SecurityConfig {

    @Resource
    private UserService userService;

    // 自定义的 UserDetailsService
    @Bean
    public UserDetailsService userDetailsService() {
        return new CustomUserDetailsService(this.userService);
    }

    // 配置密码编码器
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
                // formLogin 用于配置表单登录功能
                // permitAll() 表示允许所有用户访问表单登录相关的页面,例如登录页面、登录处理接口等。
                .formLogin(AbstractAuthenticationFilterConfigurer::permitAll)
                // 用于启用 HTTP 基本认证,withDefaults() 表示使用默认的 HTTP 基本认证配置。
                .httpBasic(withDefaults());
        return http.build();
    }

}

HelloController.java 控制器

创建一个简单的控制器,提供 /hello 接口,调用该接口仅仅返回固定字符串“Hello, Spring Security!”,如下:

package com.hxstrive.spring_security.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 简单控制器
 * @author hxstrive.com
 */
@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "Hello, Spring Security!";
    }

}

SpringSecurityDemo3Application.java 入口

在项目入口类上,通过 @MapperScan 注解设置 MyBatis Mapper 的位置,如下:

package com.hxstrive.spring_security;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

/**
 * 入口类
 * @author hxstrive.com
 */
@SpringBootApplication
@EnableWebSecurity
@MapperScan(basePackages = {"com.hxstrive.spring_security.mapper"})
public class SpringSecurityDemo3Application {

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

}

运行项目

在 IDE 中运行 Spring Boot 应用程序的主类(通常是带有 @SpringBootApplication 注解的类),启动日志如下:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.9)

...
2025-05-07T16:16:02.587+08:00  INFO 22416 --- [spring_security_demo3] [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
...

项目启动成功,端口为 8080。

测试应用

打开浏览器,访问 http://localhost:8080/hello 地址,由于所有请求都需要身份验证,你会看到一个登录页面,如下图:

Spring Security 数据库认证

输入用户名(zhangsan)和密码(123456),如下图:

Spring Security 数据库认证

点击“Sign in”按钮,登录系统,访问 /hello 接口,如下图:

Spring Security 数据库认证

查看日志信息,如下:

loadUserByUsername(String username) :: zhangsan
2025-05-07T16:19:11.226+08:00  INFO 22416 --- [spring_security_demo3] [nio-8080-exec-5] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2025-05-07T16:19:12.068+08:00  INFO 22416 --- [spring_security_demo3] [nio-8080-exec-5] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@8cf13a9
2025-05-07T16:19:12.073+08:00  INFO 22416 --- [spring_security_demo3] [nio-8080-exec-5] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
loadUserByUsername(String username) :: zhangsan

明显调用了自定义的“CustomUserDetailsService”类的“loadUserByUsername()”方法。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号