当前位置: 首页 > news >正文

基于Spring Security +JWT+Redis实现登录认证的流程

整体遵循 前端请求→后端验证→生成JWT→缓存redis

大致流程
  1. 前端发起登录请求

  2. Controller接收请求

    @RequestBody接收参数调用AdminServie的login方法

  3. 参数校验

    AdminServiceImpl校验账号密码非空

  4. Spring Security认证

    • 通过AuthenticationManager的authenticate方法认证

    • 内部调用UserDetailService从数据库查询用户信息,并将结果封装到实现类AdminLogin里返回给SpringSecurity

    • Spring Security自动用BCryptPasswordEncoder比对前端传入的明文密码与数据库中的加密密码

  5. 认证成功后

    • 生成JWT令牌(通过Jwt工具类)

  6. 将用户信息缓存到Redis

  7. 认证失败处理返回对应错误信息

我的代码编写顺序
基础配置
  • 定义实体类

    • 一个数据库映射的实体

    • 一个是实现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, "登录异常,请稍后重试");}
​
​}
}   

http://www.dtcms.com/a/597541.html

相关文章:

  • 深圳做网站最好的公司什么是企业形象设计
  • 【C++基础与提高】第六章:函数——代码复用的艺术
  • 【学习记录】ros2中处理livox_ros_driver1格式的msg
  • 仙游县住房和城乡建设局网站wordpress编辑角色无法上传图片
  • 邮箱类网站模板智联招聘官方网
  • 台湾旺久PL27A1芯片参数|USB3.0对拷线方案芯片|PL27A1对拷线芯片规格书
  • 开源企业建站系统哪个好广州网站建设q479185700棒
  • 网站开发制作流程中国招商网
  • 复杂网络入门到精通5:网络动力学
  • 【论文阅读】PhotoBot: Reference-Guided Interactive Photography via Natural Language
  • Alpha稳定分布概率密度函数的MATLAB实现
  • 国内做网站好的公司淄博做网站小程序的公司
  • Python处理 “列表套字典” 结构JSON数据的三种方式对比
  • 广州市官网网站建设公司详情页模板尺寸
  • 深度学习_神经网络_损失函数基础
  • Centos7.9创建新用户,授权远程登录,禁用root远程登录
  • 柔性软风管-连续测量十分便利
  • 手机网站优化排名首页浏阳seo
  • 辽宁住房与城乡建设厅网站网站单子
  • python类的内置函数
  • chrome的Network上经常看不到网络请求,解决方案
  • 复现------
  • 专业网站建设制作多少钱江门网站建设技术托管
  • 基于MATLAB的POD-DMD联合分析实现方案
  • saas 平台架构做网站简述建设一个网站的具体步骤6
  • 均安公司网站建设wordpress tag做专题
  • 邯郸手机网站开发价格怎样找做淘宝客的网站
  • Linux系统编程——进程通信之无名管道
  • 基于springboot高校办公室行政事务管理系统【带源码和文档】
  • Amplitude使用记录