SpringBoot集成SpringSecurity+JWT从0到1搭建登录权限项目


崧峻
原创
发布时间: 2025-07-15 11:54:05 | 阅读数 0收藏数 0评论数 0
封面
本篇文章将带你从0到1完整搭建一个基于Spring Boot的用户登录认证系统,集成Spring Security与JWT,实现用户登录、Token生成与校验、登录状态拦截等功能。文章从基础环境搭建开始,逐步讲解核心配置、自定义登录逻辑与过滤器实现,帮助你深入理解认证流程与无状态登录机制。内容贴近前后端分离项目的实战需求,适合Spring Boot初学者或有一定基础的开发者快速掌握权限系统的搭建过程,为后续项目开发打下坚实基础。
1

什么是security

Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。它是 Spring 应用程序安全的事实标准。

Spring Security 是一个专注于为 Java 应用程序提供身份验证和授权的框架。与所有 Spring 项目一样,Spring Security 的真正强大之处在于它可以轻松扩展以满足自定义需求。


图中是security的支持

2

创建项目

如图1所示先创建一个maven项目 记得springboot3 jdk版本必须得是java17

如图2所示 选择需要的依赖 如图所示是我需要的后面去里面还得增加 大家根据自己的实际情况进行

----------------------------

如3视频中展示 导入了security后他会自动的帮你在访问每个接口的时候添加一个登录页面账号是user 密码是后端显示的 这时候我们还没有做任何的配置,也可以在yml里面配置让他自定义用户名密码 但是不重要我们目前不需要 这个只是给大家简单演示介绍一下

3

认证用户实体信息

首先导入 fastjson 的maven 因为需要用到 @JSONField(serialize = false) 这个注解 按道理来讲password字段不应该返回给前端


新建用户实体类(根据你自己的实际情况创建) 例子如下

package com.doit.springsecuritytest.domain;


import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;

import java.io.Serializable;

/**
* 用户信息表
*
* @author GJQ
* @TableName user_info
*/
@Data
public class UserInfo implements Serializable {

/**
* 用户信息主键Id
*/
private Long userInfoId;

/**
* 用户名/账号
*/
private String username;


/**
* 密码
*/
@JSONField(serialize = false)
private String password;

/**
* 头像
*/

private String headSculpture;

/**
* 昵称
*/
private String nickname;

/**
* 电话号码
*/
private String phoneNumber;

/**
* 邮箱
*/
private String email;

}


UserDetails 是 Spring Security 中的一个核心接口,用于表示系统中“已认证用户”的详细信息。Spring Security 在进行身份认证时会使用它来加载和存储用户信息。


实现UserDetails接口代码

package com.doit.springsecuritytest.domain;

import com.alibaba.fastjson2.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;

/**
* @author gjq
* @version 1.0
* @description: TODO
* @date 2025/7/12 11:56
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginUserDetails implements UserDetails {

/**
* token
*/
private String token;

/**
* 用户信息
*/
private UserInfo userInfo;

/**
* 权限
*
* @return 权限
*/
@JSONField(serialize = false)
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}

/**
* 密码
*
* @return 密码
*/
@JSONField(serialize = false)
@Override
public String getPassword() {
return userInfo.getPassword();
}

/**
* 用户名
*
* @return 账号
*/
@JSONField(serialize = false)
@Override
public String getUsername() {
return this.userInfo.getUsername();
}

/**
* 账户未过期
*
* @return true
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonExpired() {
return true;
}

/**
* 账户未锁定
*
* @return true
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonLocked() {
return true;
}

/**
* 凭据未过期
* 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
*
* @return true
*/
@JSONField(serialize = false)
@Override
public boolean isCredentialsNonExpired() {
return true;
}

/**
* 已启用
*
* @return true
*/
@JSONField(serialize = false)
@Override
public boolean isEnabled() {
return true;
}
}


大家可以看一下项目结构 项目结构如图所示

4

认证用户信息接口-介绍

  1. 创建一个service 实现UserDetailsService 这个接口 这个接口用于当用户尝试登录时,Spring Security 会调用你实现的 loadUserByUsername 方法:传入用户名 ,返回一个 UserDetails 对象 ,如果找不到用户,抛出 UsernameNotFoundException 如图1
  2. 考虑到大家 有可能有邮箱登录 手机号登录 或者第三方登录的需求 所以我们也要实现我们自己的service 接口 loadUserByUsername只用于账号密码登录方法 如图2
  3. 然后因为我们实现了UserDetails 这个实体类 我们可以把这个接口返回的参数改成我们的代码如下
package com.doit.springsecuritytest.service.impl;

import com.doit.springsecuritytest.domain.LoginUserDetails;
import com.doit.springsecuritytest.service.LoginService;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

/**
* @author gjq
* @version 1.0
* @description: TODO
* @date 2025/7/12 10:46
*/
@Service
public class LoginServiceImpl implements UserDetailsService,LoginService {

@Override
public LoginUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return null;
}
}


我们可以在里面写一些判断和查询 然后放到实体类中并返回 例如下面代码 效果如第三个媒体视频所示

后面这里大家可以自行更改为查询数据库的逻辑

@Override
public LoginUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserInfo info = new UserInfo();
info.setUsername("user");
// 加密
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
info.setPassword(bCryptPasswordEncoder.encode("123123"));
return new LoginUserDetails("", info);
}


5

redis 配置

yml 配置

server:
port: 8080
spring:
data:
# redis相关配置
redis:
# redis数据库索引
database: ${REDIS_DB:4}
# ip地址
host: ${REDIS_HOST:127.0.0.1}
# 端口号
port: ${REDIS_PORT:6379}
# 密码
password: ${REDIS_PWD:123456}
# 连接超时时间
timeout: 5000



redis 工具类

package com.doit.springsecuritytest.utils;

import jakarta.annotation.Resource;
import org.springframework.data.redis.connection.stream.ObjectRecord;
import org.springframework.data.redis.connection.stream.RecordId;
import org.springframework.data.redis.connection.stream.StreamRecords;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
* Redis工具类,使用之前请确保RedisTemplate成功注入
*
* @author GJQ
* @date 2023/6/8 17:38
*/
@Component
public class RedisUtils {

public RedisUtils() {
}

@Resource
private RedisTemplate<String, Object> redisTemplate;

/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @return true=设置成功;false=设置失败
*/
public boolean expire(final String key, final long timeout) {

return expire(key, timeout, TimeUnit.SECONDS);
}

/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @param unit 时间单位
* @return true=设置成功;false=设置失败
*/
public boolean expire(final String key, final long timeout, final TimeUnit unit) {
Boolean ret = redisTemplate.expire(key, timeout, unit);
return ret != null && ret;
}

/**
* 删除单个key
*
* @param key 键
* @return true=删除成功;false=删除失败
*/
public boolean del(final String key) {

Boolean ret = redisTemplate.delete(key);
return ret != null && ret;
}

/**
* 删除多个key
*
* @param keys 键集合
* @return 成功删除的个数
*/
public long del(final Collection<String> keys) {

Long ret = redisTemplate.delete(keys);
return ret == null ? 0 : ret;
}

/**
* 存入普通对象
*
* @param key Redis键
* @param value 值
*/
public void set(final String key, final Object value) {

redisTemplate.opsForValue().set(key, value, 1, TimeUnit.MINUTES);
}


/**
* 存入普通对象
*
* @param key 键
* @param value 值
* @param timeout 有效期,单位秒
*/
public void set(final String key, final Object value, final long timeout) {
redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
}

/**
* 获取普通对象
*
* @param key 键
* @return 对象
*/
public Object get(final String key) {
return redisTemplate.opsForValue().get(key);
}

// 存储Hash操作

/**
* 往Hash中存入数据
*
* @param key Redis键
* @param hKey Hash键
* @param value 值
*/
public void hPut(final String key, final String hKey, final Object value) {

redisTemplate.opsForHash().put(key, hKey, value);
}

/**
* 往Hash中存入多个数据
*
* @param key Redis键
* @param values Hash键值对
*/
public void hPutAll(final String key, final Map<String, Object> values) {

redisTemplate.opsForHash().putAll(key, values);
}

/**
* 获取Hash中的数据
*
* @param key Redis键
* @param hKey Hash键
* @return Hash中的对象
*/
public Object hGet(final String key, final String hKey) {

return redisTemplate.opsForHash().get(key, hKey);
}

/**
* 获取多个Hash中的数据
*
* @param key Redis键
* @param hKeys Hash键集合
* @return Hash对象集合
*/
public List<Object> hMultiGet(final String key, final Collection<Object> hKeys) {

return redisTemplate.opsForHash().multiGet(key, hKeys);
}

// 存储Set相关操作

/**
* 往Set中存入数据
*
* @param key Redis键
* @param values 值
* @return 存入的个数
*/
public long sSet(final String key, final Object... values) {
Long count = redisTemplate.opsForSet().add(key, values);
return count == null ? 0 : count;
}

/**
* 删除Set中的数据
*
* @param key Redis键
* @param values 值
* @return 移除的个数
*/
public long sDel(final String key, final Object... values) {
Long count = redisTemplate.opsForSet().remove(key, values);
return count == null ? 0 : count;
}

// 存储List相关操作

/**
* 往List中存入数据
*
* @param key Redis键
* @param value 数据
* @return 存入的个数
*/
public long lPush(final String key, final Object value) {
Long count = redisTemplate.opsForList().rightPush(key, value);
return count == null ? 0 : count;
}

/**
* 往List中存入多个数据
*
* @param key Redis键
* @param values 多个数据
* @return 存入的个数
*/
public long lPushAll(final String key, final Collection<Object> values) {
Long count = redisTemplate.opsForList().rightPushAll(key, values);
return count == null ? 0 : count;
}

/**
* 往List中存入多个数据
*
* @param key Redis键
* @param values 多个数据
* @return 存入的个数
*/
public long lPushAll(final String key, final Object... values) {
Long count = redisTemplate.opsForList().rightPushAll(key, values);
return count == null ? 0 : count;
}

/**
* 从List中获取begin到end之间的元素
*
* @param key Redis键
* @param start 开始位置
* @param end 结束位置(start=0,end=-1表示获取全部元素)
* @return List对象
*/
public List<Object> lGet(final String key, final int start, final int end) {
return redisTemplate.opsForList().range(key, start, end);
}

/**
* 获取当前key的剩余过期时间
* 以秒为单位
*
* @param key Redis键
* @return 秒数
*/
public Long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}

/**
* 获取当前key的剩余过期时间
*
* @param timeUnit 时间单位
* @param key redis键
* @return 剩余时间
*/
public Long getExpire(String key, TimeUnit timeUnit) {
return redisTemplate.getExpire(key, timeUnit);
}

/**
* 判断key是否存在
*
* @param key 键
* @return 存在为true 不存在是false
*/
public Boolean getKeyExists(String key) {
return redisTemplate.hasKey(key);
}

/**
* 往stream里面插入单个数据
* @param key 键
* @param value 值
*/
public RecordId xAddStream(String key, Object value) {
// 设置key 和值
ObjectRecord<String, Object> record = StreamRecords.newRecord()
.in(key)
.ofObject(value)
.withId(RecordId.autoGenerate());
// 插入到redis中并返回唯一id
RecordId recordId = redisTemplate.opsForStream()
.add(record);
return recordId;
}
}


不报错的话就是redis连接成功

6

jwt 接口配置

导入 工具包 和jwt 依赖

<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.21</version>
</dependency>

<!-- jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>


jwt工具类

package com.doit.springsecuritytest.service.impl;

import cn.hutool.core.util.IdUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.doit.springsecuritytest.domain.LoginUserDetails;
import com.doit.springsecuritytest.utils.RedisUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
* @author GJQ
* @date 2023/6/28 10:19
* jwt存储逻辑层
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class JwtTokenServiceImpl {

// redis工具类
private final RedisUtils redisUtil;

// 过期时间
private final Integer EXPIRES_TIME = 1000 * 60 * 60 * 24;

// 密钥 大家自行设置
private final String secretKey = "abcdefg";

// 存储到redis中到key
private final String LOGIN_TOKEN_KEY = "login_token_key:";

/**
* 令牌请求头的key
*/
public static final String TOKEN_HEADER = "Authorization";

/**
* 令牌前缀
*/
public static final String TOKEN_PREFIX = "Bearer ";

/**
* 创建token令牌
*
* @param loginUserDetails
*/
public String createTokenJwt(LoginUserDetails loginUserDetails) {
Map<String, Object> headerClaims = new HashMap<>();
headerClaims.put("alg", "HS256");
headerClaims.put("typ", "jwt");
String uuid = IdUtil.fastUUID();
// 创建jwt
String jwt = JWT.create().withHeader(headerClaims)
//设置签发人
.withIssuer(loginUserDetails.getUsername())
//签发时间
.withIssuedAt(new Date())
// uuid
.withClaim(LOGIN_TOKEN_KEY, uuid)
//自定义声明
.withClaim("userId", loginUserDetails.getUsername())
//签名算法 使用HS256
.sign(Algorithm.HMAC256(secretKey));
// 存储token uuid
loginUserDetails.setToken(uuid);
// 刷新/存储token
refreshToken(loginUserDetails);
return TOKEN_PREFIX + jwt;
}


/**
* redis存储的token加个前缀
*
* @param request
* @return key
*/
public String getToken(HttpServletRequest request) {
//获取未解码的token
String encoderToken = request.getHeader(TOKEN_HEADER);
// 判断是否为null
if (encoderToken!=null && !encoderToken.isEmpty()) {
// 解码
String tokenBearer = URLDecoder.decode(encoderToken, StandardCharsets.UTF_8);
if (tokenBearer != null && tokenBearer.startsWith(TOKEN_PREFIX)) {
// 拆分token
return tokenBearer.replace(TOKEN_PREFIX, "");
}
}
return null;
}


/**
* 获取token的key
*
* @param uuid uuid
* @return key
*/
private String getTokenKey(String uuid) {
return LOGIN_TOKEN_KEY + uuid;
}

/**
* 如果距离过期时间小于时效的一半
* 刷新令牌有效期(刷新redis)
*/
public void refreshToken(LoginUserDetails loginUserDetails) {
// 获取token
String tokenKey = getTokenKey(loginUserDetails.getToken());
// token的剩余过期时间
Long jwtTokenExpire = redisUtil.getExpire(tokenKey, TimeUnit.SECONDS);
// 判断token是否临近过期
if (jwtTokenExpire <= (EXPIRES_TIME / 2)) {
// 刷新token
redisUtil.set(tokenKey, loginUserDetails, EXPIRES_TIME);
}
}

/**
* 获取用户身份信息
*
* @return 用户信息
*/
public LoginUserDetails getLoginUser(HttpServletRequest request) {
// 获取请求携带的令牌
String token = getToken(request);
if (token!=null && !token.isEmpty()) {
try {
DecodedJWT jwt = parseToken(token);
// 获取uuid
String uuid = jwt.getClaim(LOGIN_TOKEN_KEY).asString();
// 获取token redis的key
String userKey = getTokenKey(uuid);
// 查询redis的值
return (LoginUserDetails) redisUtil.get(userKey);
} catch (Exception e) {
log.error("获取用户信息异常'{}'", e.getMessage());
}
}
return null;
}

/**
* 从令牌中获取数据声明
*
* @param token 令牌
* @return 数据声明
*/
private DecodedJWT parseToken(String token) {
// 创建校验器
JWTVerifier build = JWT.require(Algorithm.HMAC256(secretKey)).build();
// 校验token
DecodedJWT verify = build.verify(token);
return verify;

}
}


jwt token效果如图2

7

jwt 拦截器配置

package com.doit.springsecuritytest.filter;


import com.doit.springsecuritytest.domain.LoginUserDetails;
import com.doit.springsecuritytest.service.impl.JwtTokenServiceImpl;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

/**
* 自定义过滤器
* 在账号密码校验之前解析出来把数据存储进去
*
* @author GJQ
* @date 2023/6/27 14:19
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

private final JwtTokenServiceImpl jwtTokenService;

@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
// 获取用户信息
LoginUserDetails loginUser = jwtTokenService.getLoginUser(request);

// 校验token是否正确
if (loginUser != null) {
// redis中数据不为null
// 刷新令牌有效期
jwtTokenService.refreshToken(loginUser);
// UsernamePasswordAuthenticationToken Spring Security 中用于封装用户名密码认证信息的一个类 把身份信息存储进去
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
// 为details属性赋值
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 将认证信息存储在 SecurityContextHolder 中
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
// 转发给下一个过滤器
chain.doFilter(request, response);
}
}


目录结构如图所示

8

security 配置


首先我们得 配置密码的加密bean

/**
* 强散列哈希加密实现
*
* @return 加密后的密码
*/
@Bean
PasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}


然后 这些是security的常用配置


配置方法

功能说明

http.csrf(AbstractHttpConfigurer::disable)

关闭 CSRF 保护(适用于前后端分离)

http.formLogin(AbstractHttpConfigurer::disable)

关闭默认登录页面(使用自定义 JWT 登录)

http.logout(AbstractHttpConfigurer::disable)

关闭默认登出功能(使用前端控制登出)

http.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))

设置为无状态登录(禁用 Session,适配 JWT)

http.addFilterBefore(new OptionsFilter(), WebAsyncManagerIntegrationFilter.class)

添加自定义过滤器处理 OPTIONS 预检请求(跨域支持)

http.cors(withDefaults())

启用跨域支持(CORS)

http.authorizeHttpRequests(auth -> auth.requestMatchers(HttpMethod.POST, "/system/login/**").permitAll())

放行登录接口 POST /system/login/**,允许匿名访问

http.authorizeHttpRequests(auth -> auth.requestMatchers("/file/**", "/druid/**").permitAll())

放行通用文件下载、Druid 控制台等接口

http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())

所有其他接口都需要认证

http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)

添加 JWT 认证过滤器,验证 Token 并设置认证信息

http.exceptionHandling(e -> e.authenticationEntryPoint(myAuthenticationEntryPoint))

设置未认证时的异常处理器(返回自定义 JSON 提示)

http.build()

构建并返回完整的 SecurityFilterChain 安全配置


以下是我的配置

package com.doit.springsecuritytest.config;

import com.doit.springsecuritytest.filter.JwtAuthenticationTokenFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
* spring security 的配置类
*
* @author GJQ
* @date 2023/6/8 14:36
*/
//启用WebSecurity配置
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
@RequiredArgsConstructor
public class SecurityConfig {

private final JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// 关闭csrf保护
http.csrf(AbstractHttpConfigurer::disable);
// 禁用默认登录页
http.formLogin(AbstractHttpConfigurer::disable);
// 禁用默认登出页
http.logout(AbstractHttpConfigurer::disable);
// 关闭session
http.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
// 登陆接口允许匿名用户访问
http.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
.requestMatchers(HttpMethod.GET, "/login").permitAll());
// 除了上面的所有请求都需要认证
http.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests.anyRequest().authenticated());
// jwtAuthenticationTokenFilter 过滤器在 UsernamePasswordAuthenticationFilter 之前
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}


/**
* 强散列哈希加密实现
*
* @return 加密后的密码
*/
@Bean
PasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}

}

加完配置就如图变成了一个接口 就没有login页面了 如图所示

9

controller层验证

package com.doit.springsecuritytest.controller;

import com.doit.springsecuritytest.domain.LoginUserDetails;
import com.doit.springsecuritytest.service.impl.JwtTokenServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author gjq
* @version 1.0
* @description: 登录接口
* @date 2025/7/12 09:56
*/
@RestController
@RequiredArgsConstructor
public class LoginController {

// 构建 AuthenticationManager
private final AuthenticationManagerBuilder authenticationManagerBuilder;

private final JwtTokenServiceImpl jwtTokenService;

@GetMapping("/login")
public String login(String username,String password){
// 用户名密码校验
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(username, password);
// 认证
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
// 获取数据
LoginUserDetails loginUserDetails = (LoginUserDetails) authentication.getPrincipal();
// 获取token 并存储
String jwt = jwtTokenService.createTokenJwt(loginUserDetails);
return jwt;
}

@GetMapping("/t")
public String test(){
return "123";
}
}


这种登录的代码 大家可以看一下 通过 UsernamePasswordAuthenticationToken authenticationToken =

new UsernamePasswordAuthenticationToken(username, password); 这个 判断密码是否正确 进行验证 然后获取登录信息 解析为jwt token 返回给前端 或者后端直接存储到cookie 中也行


10

效果演示

  1. 首先我们如图1所示正常访问 输入我们设置好的账号和密码 登录成功后我们可以看到redis中已经有我们的用户信息了 如图2
  2. 然后复制返回的token 把它放到请求头的 Authorization里面
  3. 然后我们进行对比 图4 和图5 一个是有请求头属于登录了 一个是没有请求头属于没登录的效果
11

小结

  1. 如图1所示是第一次登录的流程 相当于只要用户认证成功那么就把信息往redis中存储一份 然后前端存储起来
  2. 然后访问接口如图2所示 携带token进行请求,解析token里uuid 通过uuid获取redis中的值 最后判断是否有值来访问接口
阅读记录0
点赞0
收藏0
禁止 本文未经作者允许授权,禁止转载
猜你喜欢
评论/提问(已发布 0 条)
评论 评论
收藏 收藏
分享 分享
pdf下载 下载