基于Spring Security +JWT+Redis实现登录认证的流程
整体遵循 前端请求→后端验证→生成JWT→缓存redis
大致流程
-
前端发起登录请求
-
Controller接收请求
@RequestBody接收参数调用AdminServie的login方法
-
参数校验
AdminServiceImpl校验账号密码非空
-
Spring Security认证
-
通过
AuthenticationManager的authenticate方法认证 -
内部调用UserDetailService从数据库查询用户信息,并将结果封装到实现类AdminLogin里返回给SpringSecurity
-
Spring Security自动用
BCryptPasswordEncoder比对前端传入的明文密码与数据库中的加密密码
-
-
认证成功后
-
生成JWT令牌(通过Jwt工具类)
-
-
将用户信息缓存到Redis
-
认证失败处理返回对应错误信息
我的代码编写顺序
基础配置
-
定义实体类
-
一个数据库映射的实体
-
一个是实现UserDetails接口封装用户信息和权限的实体,——这个是认证需要的
-
-
一部分工具类
-
统一返回响应和错误码(这个我基本都在用同样的,所以没怎么改过,直接套用)
-
Redis基础常用操作工具类
-
-
配置SpringSecurity核心组件
-
配置SecurityConfig:PasswordEncoder、AuthenticationManager和过滤链SecurityFilterChain
-
-
用户信息查询
-
mybatis-plus自动生成+ 实现UserDetailsService接口重写loadUserByUsername方法,封装到Adminlogin
-
-
实现Jwt工具类
-
生成、验证、解析
-
-
配置RedisConfig
-
配置RedisTemplate,指定序列化方式,设置键值的序列化方shi
-
-
编写Controller
-
先编写登录接口(根据问题找答案),倒着写业务层
-
-
实现登录逻辑业务
-
参数校验
-
authenticationManager.authenticate()认证
-
认证失败返回
-
生成token
-
缓存到redis
-
返回结果
-
-
apifox调接口测试
-
出现bug就改
-
上代码
-
依赖(依赖因为我用的分模块设计,就直接看版本吧和名字吧)

-
用户实体类
-
AdminLogin
package com.yyq.domain; 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; import java.util.List; /*** @Author 写你的名字* @Date 2025/11/10 上午11:46 (可以根据需要修改)* @Version 1.0 (版本号)*/ @Data @AllArgsConstructor @NoArgsConstructor public class AdminLogin implements UserDetails {private Admin admin;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;} @Overridepublic String getPassword() {return admin.getPassword();} @Overridepublic String getUsername() {return admin.getNickName();} @Overridepublic boolean isAccountNonExpired() {return true;} @Overridepublic boolean isAccountNonLocked() {return true;} @Overridepublic boolean isCredentialsNonExpired() {return true;} @Overridepublic boolean isEnabled() {return true;} }
-
错误码和响应
package com.yyq.util; import com.fasterxml.jackson.annotation.JsonInclude; import com.yyq.util.ResultCode; import lombok.Data; /*** @Author 写你的名字* @Date 2025/11/5 上午10:45 (可以根据需要修改)* @Version 1.0 (版本号)*/ @Data @JsonInclude(JsonInclude.Include.NON_NULL) public class R<T> {//返回码private Integer code;//返回消息private String message;//返回数据private T data; public R() {} public R(Integer code, String message, T data) {this.code = code;this.message = message;this.data = data;} protected static <T> R<T> build(T data){R<T> result = new R<T>();if(data!=null){result.setData(data);}return result; }private static <T> R<T> build(Integer code, String message, T data) { R<T> responseResult = new R<>();responseResult.setCode(code);responseResult.setMessage(message);responseResult.setData(data);return responseResult;} private static <T> R<T> build(Integer code, String message) { R<T> responseResult = new R<>();responseResult.setCode(code);responseResult.setMessage(message);return responseResult;}// 成功的public static <T> R<T> ok(T data){return build(ResultCode.SUCCESS.getCode(),ResultCode.SUCCESS.getMsg(),data);}//成功的不带数据public static <T> R<T> ok(){return build(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg());}//成功的带数据public static <T> R<T> ok(String customMsg, T data) {return build(ResultCode.SUCCESS.getCode(), customMsg, data);}//错误的不带数据public static <T> R<T> er(ResultCode resultCode) {return build(resultCode.getCode(), resultCode.getMsg(), null);} /*** 错误的自定义错误码 + 消息* R.er(1007, "验证码过期")*/public static <T> R<T> er(Integer customCode, String customMsg) {return build(customCode, customMsg, null);} /*** 错误的自定义消息 + 错误数据* R.er("参数错误", Arrays.asList("account不能为空"))*/public static <T> R<T> er(ResultCode resultCode, T errorData) {return build(resultCode.getCode(), resultCode.getMsg(), errorData);}public Integer getCode() {return code;} public void setCode(Integer code) {this.code = code;} public String getMessage() {return message;} public void setMessage(String message) {this.message = message;} public T getData() {return data;} public void setData(T data) {this.data = data;} }package com.yyq.util;/*** @Author 写你的名字* @Date 2025/11/5 上午11:51 (可以根据需要修改)* @Version 1.0 (版本号)*/public enum ResultCode {SUCCESS(200,"操作成功"),// 客户端错误PARAM_ERROR(1001, "参数错误"),LOGIN_FAILED(1002, "登录失败"),NEED_LOGIN(1003, "需要登录"),DATA_NOT_FOUND(1004, "数据不存在"),NOT_FOUND(1005, "接口不存在"),UPLOAD_FAILED(1006, "文件上传失败"),// 服务器错误SYSTEM_ERROR(2001, "系统错误"),DB_ERROR(2002, "数据库操作错误"),// 业务错误OPERATION_FAILED(3001, "业务操作失败"),ACCOUNT_EXIST(3002, "账号已存在"),PERMISSION_DENIED(3003, "权限不足"),HTTP_401_UNAUTHORIZED(401, "未授权"), // 对应 HTTP 401 状态码HTTP_403_FORBIDDEN(403, "无权限访问"), // 对应 HTTP 403 状态码HTTP_404_NOT_FOUND(404, "接口不存在"), // 对应 HTTP 404 状态码HTTP_500_INTERNAL_ERROR(500, "服务器内部错误"); // 对应 HTTP 500 状态码private int code;private String msg;public int getCode() {return code;}public String getMsg() {return msg;}ResultCode(int code, String msg) {this.code = code;this.msg = msg;} }-
Redis常用操作
package com.yyq.util; /*** @Author 写你的名字* @Date 2025/11/10 下午4:57 (可以根据需要修改)* @Version 1.0 (版本号)*/ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.util.*; import java.util.concurrent.TimeUnit; /*** Redis工具类,封装常用Redis操作*/ @Component public class RedisUtil {@Autowiredprivate RedisTemplate<String, Object> redisTemplate; // ============================ 字符串操作 ============================ /*** 设置缓存* @param key 键* @param value 值* @return 是否成功*/public boolean set(String key, Object value) {try {redisTemplate.opsForValue().set(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 仅当键不存在时设置值(SETNX)* @param key 键* @param value 值* @return true=设置成功,false=键已存在*/public boolean setIfAbsent(String key, Object value) {try {Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value);return result != null && result;} catch (Exception e) {e.printStackTrace();return false;}} /*** 仅当键不存在时设置值,并指定过期时间*/public boolean setIfAbsent(String key, Object value, long time) {try {if (time <= 0) {throw new IllegalArgumentException("过期时间必须大于0");}Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);return result != null && result;} catch (Exception e) {e.printStackTrace();return false;}} /*** 设置缓存并指定过期时间* @param key 键* @param value 值* @param time 过期时间(秒)* @return 是否成功*/public boolean set(String key, Object value, long time) {try {if (time > 0) {redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);} else {set(key, value);}return true;} catch (Exception e) {e.printStackTrace();return false;}} /*** 获取缓存* @param key 键* @return 值*/public Object get(String key) {return key == null ? null : redisTemplate.opsForValue().get(key);} /*** 删除缓存* @param key 键* @return 是否成功*/public boolean delete(String key) {Boolean result = redisTemplate.delete(key);return result != null && result;} /*** 批量删除缓存* @param keys 键的集合* @return 删除的数量*/public long delete(Collection<String> keys) {return redisTemplate.delete(keys);} /*** 设置过期时间* @param key 键* @param time 过期时间(秒)* @return 是否成功*/public boolean expire(String key, long time) {if (time <= 0) {throw new IllegalArgumentException("过期时间必须大于0");}Boolean result = redisTemplate.expire(key, time, TimeUnit.SECONDS);return result != null && result;} /*** 获取过期时间* @param key 键* @return 过期时间(秒),-1表示永久有效,-2表示键不存在*/public long getExpire(String key) {Long expire = redisTemplate.getExpire(key, TimeUnit.SECONDS);return expire == null ? -2 : expire;} /*** 判断键是否存在* @param key 键* @return 是否存在*/public boolean hasKey(String key) {Boolean hasKey = redisTemplate.hasKey(key);return hasKey != null && hasKey;} // ============================ 哈希操作 ============================ /*** 向哈希表中放入一个键值对* @param key 哈希表键* @param item 哈希项键* @param value 值* @return 是否成功*/public boolean hset(String key, String item, Object value) {try {redisTemplate.opsForHash().put(key, item, value);return true;} catch (Exception e) {e.printStackTrace();return false;}} /*** 向哈希表中放入一个键值对,并设置过期时间* @param key 哈希表键* @param item 哈希项键* @param value 值* @param time 过期时间(秒)* @return 是否成功*/public boolean hset(String key, String item, Object value, long time) {try {redisTemplate.opsForHash().put(key, item, value);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}} /*** 获取哈希表中指定项的值* @param key 哈希表键* @param item 哈希项键* @return 值*/public Object hget(String key, String item) {return redisTemplate.opsForHash().get(key, item);} /*** 获取哈希表中所有键值对* @param key 哈希表键* @return 键值对集合*/public Map<Object, Object> hgetAll(String key) {return redisTemplate.opsForHash().entries(key);} /*** 删除哈希表中的指定项* @param key 哈希表键* @param items 哈希项键数组* @return 删除的数量*/public long hdelete(String key, Object... items) {return redisTemplate.opsForHash().delete(key, items);} // ============================ 列表操作 ============================ /*** 向列表头部插入元素* @param key 键* @param value 值* @return 列表长度*/public long lpush(String key, Object value) {return redisTemplate.opsForList().leftPush(key, value);} /*** 向列表尾部插入元素* @param key 键* @param value 值* @return 列表长度*/public long rpush(String key, Object value) {return redisTemplate.opsForList().rightPush(key, value);} /*** 获取列表指定范围内的元素* @param key 键* @param start 开始索引(0开始)* @param end 结束索引(-1表示最后一个元素)* @return 元素列表*/public List<Object> lrange(String key, long start, long end) {return redisTemplate.opsForList().range(key, start, end);} /*** 移除并获取列表第一个元素* @param key 键* @return 第一个元素*/public Object lpop(String key) {return redisTemplate.opsForList().leftPop(key);} /*** 移除并获取列表最后一个元素* @param key 键* @return 最后一个元素*/public Object rpop(String key) {return redisTemplate.opsForList().rightPop(key);} /*** 获取列表长度* @param key 键* @return 长度*/public long lsize(String key) {Long size = redisTemplate.opsForList().size(key);return size == null ? 0 : size;} // ============================ 集合操作 ============================ /*** 向集合中添加元素* @param key 键* @param values 元素数组* @return 添加的元素数量*/public long sadd(String key, Object... values) {return redisTemplate.opsForSet().add(key, values);} /*** 获取集合中的所有元素* @param key 键* @return 元素集合*/public Set<Object> sgetAll(String key) {return redisTemplate.opsForSet().members(key);} /*** 判断元素是否在集合中* @param key 键* @param value 元素* @return 是否存在*/public boolean scontains(String key, Object value) {Boolean contains = redisTemplate.opsForSet().isMember(key, value);return contains != null && contains;} /*** 从集合中移除元素* @param key 键* @param values 元素数组* @return 移除的元素数量*/public long sremove(String key, Object... values) {return redisTemplate.opsForSet().remove(key, values);} /*** 获取集合大小* @param key 键* @return 大小*/public long ssize(String key) {Long size = redisTemplate.opsForSet().size(key);return size == null ? 0 : size;} // ============================ 有序集合操作 ============================ /*** 向有序集合中添加元素* @param key 键* @param value 元素* @param score 分数* @return 是否成功*/public boolean zadd(String key, Object value, double score) {return redisTemplate.opsForZSet().add(key, value, score);} /*** 根据分数范围获取有序集合元素* @param key 键* @param min 最小分数* @param max 最大分数* @return 元素集合*/public Set<Object> zrangeByScore(String key, double min, double max) {return redisTemplate.opsForZSet().rangeByScore(key, min, max);} /*** 获取元素在有序集合中的排名(从小到大)* @param key 键* @param value 元素* @return 排名(0开始)*/public Long zrank(String key, Object value) {return redisTemplate.opsForZSet().rank(key, value);} /*** 从有序集合中移除元素* @param key 键* @param values 元素数组* @return 移除的元素数量*/public long zremove(String key, Object... values) {return redisTemplate.opsForZSet().remove(key, values);} /*** 获取有序集合大小* @param key 键* @return 大小*/public long zsize(String key) {Long size = redisTemplate.opsForZSet().size(key);return size == null ? 0 : size;} } -
-
Spring security
package com.yyq.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; 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.configuration.WebSecurityConfiguration; import org.springframework.security.config.authentication.PasswordEncoderParser; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; /*** @Author 写你的名字* @Date 2025/11/7 上午10:47 (可以根据需要修改)* @Version 1.0 (版本号)*/ @Configuration @EnableWebSecurity public class SecurityConfig{//密码加密存储@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();} @Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {return config.getAuthenticationManager();}// 配置安全过滤链@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(auth -> auth.requestMatchers("/admin/login").permitAll() // 登录接口允许匿名访问.anyRequest().authenticated() // 其他接口需要认证).csrf(csrf -> csrf.disable()); return http.build();} }
-
UserLoginServiceImpl封装
-
mybatis-plus generator
-
实现类
package com.yyq.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.yyq.domain.Admin; import com.yyq.domain.AdminLogin; import com.yyq.mapper.AdminMapper; 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.Service; import java.util.Objects; /*** @Author 写你的名字* @Date 2025/11/10 上午11:41 (可以根据需要修改)* @Version 1.0 (版本号)*/ @Service public class UserLoginServiceImpl implements UserDetailsService {@Autowiredprivate AdminMapper adminMapper;@Overridepublic UserDetails loadUserByUsername(String account) throws UsernameNotFoundException {//todo:查询用户信息LambdaQueryWrapper<Admin> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(Admin::getAccount,account);Admin admin = adminMapper.selectOne(queryWrapper);if(Objects.isNull(admin)){throw new RuntimeException("用户名或密码错误");}//查询对应的权限信息//把数据封装成UserDetails返回AdminLogin adminLogin = new AdminLogin(admin);return adminLogin;} } -
-
Jwt工具类
package com.yyq.util;
import com.mysql.cj.util.StringUtils;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/*** JWT工具类,提供生成、验证和解析JWT令牌的功能* @Author* @Date 2025/11/10 下午2:28* @Version 1.0*/
@Component
public class JwtHelper {
@Value("${jwt.token.tokenSignKey}")private String tokenSignKey;
// 获取签名密钥public SecretKey getSecretKey() {return Keys.hmacShaKeyFor(tokenSignKey.getBytes(StandardCharsets.UTF_8));}
/*** 创建JWT令牌,默认7天有效期* @param id 用户ID* @param name 用户名* @return 生成的令牌字符串*/public String createToken(Integer id, String name) {return createToken(id, name, 7);}
/*** 创建JWT令牌,可指定有效期* @param id 用户ID* @param name 用户名* @param days 有效期(天)* @return 生成的令牌字符串*/public String createToken(Integer id, String name, int days) {Calendar calendar = Calendar.getInstance();calendar.add(Calendar.DATE, days);
Map<String, Object> claims = new HashMap<>();claims.put("id", id);
return Jwts.builder().setClaims(claims) // 自定义声明.setSubject(name) // 主题(用户名).setIssuedAt(new Date()) // 签发时间.setExpiration(calendar.getTime()) // 过期时间.signWith(getSecretKey(), SignatureAlgorithm.HS256).compact();}
/*** 验证令牌是否有效* @param token JWT令牌* @return 有效返回true,否则返回false*/public boolean verify(String token) {if (StringUtils.isNullOrEmpty(token)) {return false;}try {
Jwts.parserBuilder().setSigningKey(getSecretKey()).build().parseClaimsJws(token);return true;} catch (SignatureException e) {return false; // 签名错误} catch (ExpiredJwtException e) {return false; // 令牌过期} catch (MalformedJwtException e) {return false; // 令牌格式错误} catch (InvalidClaimException e) {return false; // 声明无效} catch (JwtException e) {return false; // 其他JWT异常}}
/*** 从令牌中获取所有声明(返回Claims类型,保留标准方法)*/public Claims getAllClaims(String token) {if (StringUtils.isNullOrEmpty(token)) {return null;}try {Jws<Claims> claimsJwts = Jwts.parserBuilder().setSigningKey(getSecretKey()).build().parseClaimsJws(token);return claimsJwts.getBody();} catch (Exception e) {return null;}}
/*** 从令牌中获取用户名*/public String extractUsername(String token) {Claims claims = getAllClaims(token);return claims != null ? claims.getSubject() : null;}
/*** 从令牌中获取用户ID*/public Integer extractUserId(String token) {Claims claims = getAllClaims(token);return claims != null ? (Integer) claims.get("id") : null;}
/*** 获取令牌的签发时间* @param token JWT令牌* @return 签发时间,失败返回null*/public Date extractIssuedAt(String token) {Claims claims = getAllClaims(token);return claims != null ? claims.getIssuedAt() : null;}
/*** 获取令牌的过期时间* @param token JWT令牌* @return 过期时间,失败返回null*/public Date extractExpiration(String token) {Claims claims = getAllClaims(token);return claims != null ? (Date) claims.getExpiration() : null;}/*** 检查令牌是否已过期* @param token JWT令牌* @return 已过期返回true,否则返回false*/public boolean isTokenExpired(String token){Date expiration= extractExpiration(token);return expiration !=null && expiration.before(new Date());}/*** 刷新令牌(生成新的令牌,保持相同的用户信息)* @param token 旧令牌* @return 新令牌,失败返回null*/public String refreshToken(String token){if(!verify(token)){return null;}Claims claims = getAllClaims(token);if(claims ==null){return null;}Integer id = (Integer) claims.get("id");String username = claims.getSubject().toString();return createToken(id, username);}
}
-
配置Redisconfig
package com.yyq.config; import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; /*** @Author 写你的名字* @Date 2025/11/10 下午4:47 (可以根据需要修改)* @Version 1.0 (版本号)*/ @Configuration public class RedisConfig {@Beanpublic RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){//创建RedisTemplate对象RedisTemplate<String ,Object> template = new RedisTemplate<>(); //设置连接工厂template.setConnectionFactory(factory);//设置key序列化template.setKeySerializer(new StringRedisSerializer());template.setHashKeySerializer(new StringRedisSerializer());//设置Value的序列化FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<>(Object.class);template.setValueSerializer(serializer);template.setHashValueSerializer(serializer);//返回template.afterPropertiesSet();return template;} }
-
Controller
@RestController
public class LoginController {@Autowiredprivate AdminService adminService;@PostMapping("admin/login")public R login(@RequestBody Admin admin){return adminService.login(admin);
}
-
业务层
package com.yyq.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yyq.domain.Admin;
import com.yyq.domain.AdminLogin;
import com.yyq.service.AdminService;
import com.yyq.mapper.AdminMapper;
import com.yyq.util.JwtHelper;
import com.yyq.util.R;
import com.yyq.util.RedisUtil;
import com.yyq.util.ResultCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
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.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* @author 29278
* @description 针对表【admin(管理员表)】的数据库操作Service实现
* @createDate 2025-11-10 10:09:14
*/
@Service
public class AdminServiceImpl extends ServiceImpl<AdminMapper, Admin>implements AdminService{@Autowiredprivate JwtHelper jwtHelper;@Autowiredprivate RedisUtil redisUtil;@Autowiredprivate AuthenticationManager authenticationManager;
@Overridepublic R login(Admin admin) {// 1. 校验请求参数(避免空指针)if (admin == null || admin.getAccount() == null || admin.getPassword() == null) {return R.er(ResultCode.PARAM_ERROR, "账号或密码不能为空");}//AuthenticationManager authenticate 进行用户认证try {UsernamePasswordAuthenticationToken authenticationToken = UsernamePasswordAuthenticationToken.unauthenticated(
admin.getAccount(),admin.getPassword());//执行认证Authentication authenticate = authenticationManager.authenticate(authenticationToken);//认证失败处理if (Objects.isNull(authenticate)) {throw new RuntimeException(("登录失败"));}//认证成功后,获取用户信息并生成JWTAdminLogin adminLogin = (AdminLogin)authenticate.getPrincipal();Integer adminId = adminLogin.getAdmin().getAdminId();
// String account = adminLogin.getAdmin().getAccount().toString();String jwt = jwtHelper.createToken(adminId, adminLogin.getAdmin().getAccount());Map<String,Object> map = new HashMap<>();map.put("token",jwt);map.put("expireTime",7200);//存储到redis里String redisKey = "login:admin:" + adminId; boolean cacheSuccess = redisUtil.set(redisKey, adminLogin, 7200);if (!cacheSuccess) {// 日志记录:缓存失败(实际项目中应使用日志框架)System.err.println("Redis缓存用户信息失败,adminId: " + adminId);// 缓存失败不影响登录,但需提示(或降级处理)return R.ok("登录成功,但用户状态暂未同步", map);}return R.ok(map);} catch (UsernameNotFoundException e) {// 账号不存在(由UserDetailsService抛出)return R.er(ResultCode.DATA_NOT_FOUND, "账号不存在");} catch (BadCredentialsException e) {// 密码错误(Spring Security自动抛出)return R.er(ResultCode.LOGIN_FAILED, "密码错误");} catch (Exception e) {// 其他异常(如Redis连接失败、JWT生成失败等)e.printStackTrace(); // 实际项目中替换为日志输出return R.er(ResultCode.SYSTEM_ERROR, "登录异常,请稍后重试");}
}
}
