1. 数据准备
create table sys_user(
id int primary key auto_increment,
user_name varchar(32) not null unique comment '用户名',
password varchar(128) comment '密码',
nick_name varchar(32) comment '昵称',
gender enum('男','女') comment '性别',
avatar varchar(128) comment '头像',
email varchar(128) comment '邮箱',
phone varchar(16) comment '手机号',
status tinyint comment '状态',
deleted tinyint comment '删除状态',
remark varchar(1024) comment '备注',
create_time datetime default now() comment '创建时间',
update_time datetime default null comment '更新时间'
);
2.项目创建
添加依赖
3 实体类的开发
package com.kclm.springsecuritydemo03.entiy;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Collection;
/**
* @Classname SysUser
* @Description TODO
* @Date 2024/10/15 11:12
* @Created by HP
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Slf4j
public class SysUser implements Serializable, UserDetails {
private Integer id;//id
private String userName; //用户名
private String password;//密码
private String nickName;//昵称
private String gender ;//性别
private String avatar ;//头像
private String email;//邮箱
private String phone ;//手机号
private Integer status ;//状态 0禁用 1:启用
private Integer deleted ;//删除状态, 0 未删除 1删除
private String remark ;// 备注
private LocalDateTime create_time;//创建时间
private LocalDateTime update_time ;//更新时间
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getUsername() {
return this.userName;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public boolean isAccountNonExpired() {
return status==1;
}
@Override
public boolean isAccountNonLocked() {
return status==1;
}
@Override
public boolean isCredentialsNonExpired() {
return status==1;
}
@Override
public boolean isEnabled() {
return status==1;
}
}
4.Mapper接口的开发
@Mapper
public interface SysUserMapper {
SysUser findByUserName(String userName);
void save(SysUser sysUser);
}
5.开发SysUserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kclm.springsecuritydemo03.mapper.SysUserMapper">
<select id="findByUserName" resultType="SysUser">
select
id,user_name,password,nick_name,gender,avatar,email,phone,status,deleted,remark,create_time,update_time
from sys_user
where user_name=#{userName}
</select>
<insert id="save">
insert into
sys_user(user_name,password,nick_name,gender,avatar,email,phone,status,deleted,remark,create_time,update_time)
values(#{username},#{password},#{nickName},#{gender},#{avatar},#{email},#{phone},#{status},#{deleted},#{remark},#{createTime},#{updateTime})
</insert>
</mapper>
6.配置文件application.yaml
server:
port: 9091
spring:
profiles:
active: dev
mybatis:
type-aliases-package: com.kclm.springsecuritydemo03.entiy
configuration:
map-underscore-to-camel-case: true
mapper-locations: classpath:mybatis/mappers/*.xml
application.yaml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=urt8&serverTimezone=GMT%2B8
username: root
password: 123456
以上接口的测试代码
@Test
public void save(){
SysUser sysUser = SysUser.builder()
.userName("admin")
.password("123456")
.nickName("admin用户")
.gender("男")
.avatar("/static/admin.png")
.email("admin123456@16.com")
.phone("136123466578")
.status(1)
.deleted(0)
.remark("备注admin用户")
.createTime(LocalDateTime.of(2023, 10, 1, 10, 12, 10))
.updateTime(LocalDateTime.of(2024, 3, 1, 13, 12, 10)).build();
sysUserMapper.save(sysUser);
}
@Test
public void testFindByName(){
SysUser admin = sysUserMapper.findByUserName("admin");
System.out.println(admin);
}
8 开发SysUserDetailsService
@Service
@Slf4j
public class SysUserDetailsService implements UserDetailsService {
private SysUserMapper sysUserMapper;
//采用构造方法的注入方式
public SysUserDetailsService(SysUserMapper sysUserMapper) {
this.sysUserMapper = sysUserMapper;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("====》调用loadUserByUserName:{}",username);
SysUser sysUser = sysUserMapper.findByUserName(username);
return sysUser ;
}
}
9 springSecurity.java配置类
@Configuration
@EnableWebSecurity
@Slf4j
public class SecurityConfig {
private SysUserDetailsService sysUserDetailsService;
public SecurityConfig(SysUserDetailsService sysUserDetailsService) {
log.info("====>通过构造器注入 sysUserDetailsService");
this.sysUserDetailsService = sysUserDetailsService;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//关闭csrf机制
http.csrf(csrf-> csrf.disable());
http.authorizeHttpRequests(auth->auth.anyRequest().authenticated());
//走security 内部的登录页面
http.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder(){
//构建编码器
return new BCryptPasswordEncoder();
}
}
开发一个接口测试做测试
@RestController
public class HomeController {
@RequestMapping("home")
public String home(){
return "welcome home!!!";
}
}
访问localhost:9091/home 浏览器会自动重定向到登录页面
输入用户名和密码 【和数据中用户名和密码保持一致】
以上是基于springSecurity内置表单认证的
认证流程
1 .当用户提用户名和密码的时候,UsernamePasswordAuthenticationFilter创建一个UsernamePasswordAuthenticationToken,这是一种通过从HttpServletRequest提取用户名和密码的身份验证类型。
2.将UsernamePasswordAuthenticationToken传递到要进行身份验证的AuthenticationManager。
3 .AuthenticationManager对象 默认是ProvideManager 调用AuthenticationManager.authenticate() 此方法的参数就是UsernamePasswordAuthenticationToken(未认证) 返回值也是一个Authentication(已经认证过的对象--未出现异常情况)
基于非默认表单验证
1 创建SysUserDto 对象接受前端传递过来数据
@Data
public class SysUserDto {
private String username;
private String password;
}
2 登录控制器LoginConrtoller.java
@RestController
@Slf4j
@RequestMapping("user")
public class LoginController {
//引入业务类
private ISysUserService sysUserService;
public LoginController(ISysUserService sysUserService) {
this.sysUserService = sysUserService;
}
//用户登录
@PostMapping("/login")
public String login(@RequestBody SysUserDto sysUserDto){
String token = sysUserService.login(sysUserDto);
return token;
}
}
3 业务接口和实现类的开发
public interface ISysUserService {
String login(SysUserDto sysUserDto);
}
@Service
@Slf4j
public class SysUserServiceImpl implements ISysUserService {
private AuthenticationManager authenticationManager;
//通过构造器注入的方式
public SysUserServiceImpl(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
/**
* 用户登录的方法中实现自定义认证流程
*/
@Override
public String login(SysUserDto sysUserDto) {
//实现认证流程
//1 创建 UsernamePasswordAuthenticationToken对象
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(sysUserDto.getUsername(),sysUserDto.getPassword());
//2 调用 authenticationManager.authenticate()方法完成认证
Authentication authenticate =null;
try {
authenticate = authenticationManager.authenticate(authenticationToken);
}catch (AuthenticationException e){
return "用户名或密码错误";
}
//3 获取返回的用户
SysUser sysUser = (SysUser) authenticate.getPrincipal();
// 通过认证 这里返回一个字符串 后期使用jwt 生成token
String token = UUID.randomUUID().toString().replace("-","");
log.info("登录后的用户====>{}",sysUser);
return token;
}
}
4 SecurityConfig.java类
@Configuration
@EnableWebSecurity
@Slf4j
public class SecurityConfig {
private SysUserDetailsService sysUserDetailsService;
public SecurityConfig(SysUserDetailsService sysUserDetailsService) {
log.info("====>通过构造器注入 sysUserDetailsService");
this.sysUserDetailsService = sysUserDetailsService;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//关闭csrf机制
http.csrf(csrf-> csrf.disable());
http.authorizeHttpRequests(auth->
auth.requestMatchers("/user/**").permitAll()
.anyRequest().authenticated());
//走security 内部的登录页面
// http.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder){
log.info("======>创建AuthenticationManager实例:注入passwordEncoder");
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setPasswordEncoder(passwordEncoder);
//设置UserDetailsService
authenticationProvider.setUserDetailsService(sysUserDetailsService);
return new ProviderManager(authenticationProvider);
}
@Bean
public PasswordEncoder passwordEncoder(){
//构建编码器
return new BCryptPasswordEncoder();
}
}
使用postMan或者apiPost做测试
输入正确的用户名和密码:
输入错误的用户名和密码:
原理图如下
官方原文