springSecurity+jwt,简单版demo

登录controller

java 复制代码
package com.example.demo.controller;

import com.example.demo.Vo.LoginResult;
import com.example.demo.Vo.R;
import com.example.demo.entity.LoginBody;
import com.example.demo.service.AuthService;
import com.example.demo.service.CaptchaService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

/**
 * 用户认证 Controller
 */
@RestController
@RequiredArgsConstructor
public class AuthController {


    private final AuthService authService;

    private final  CaptchaService captchaService;

    /**
     * 登录接口
     */
    @PostMapping("/login")
    public R login(@RequestBody LoginBody loginBody) {
        // 1. 验证码校验 (可选)
        if (loginBody.getCaptchaCode() != null) {
            captchaService.verifyCaptcha(loginBody.getUserName(),
                                        loginBody.getCaptchaCode());
        }

        // 2. 执行登录认证
        LoginResult result = authService.login(
            loginBody.getUserName(),
            loginBody.getPassword(),
            loginBody.getCode(),
            loginBody.getUuid()
        );

        // 3. 返回 Token 和用户信息
        Map<String, Object> response = new HashMap<>();
        response.put("accessToken", result.getAccessToken());
        response.put("refreshToken", result.getRefreshToken());
        response.put("userInfo", result.getUserInfo());

        return R.ok(response);
    }

    /**
     * 退出登录
     */
//    @PostMapping("/logout")
//    public R<Void> logout(HttpServletRequest request) {
//        String token = getTokenFromRequest(request);
//        authService.logout(token);
//        return R.ok();
//    }

    /**
     * 刷新 Token
     */
    @PostMapping("/refresh")
    public R refreshToken(
            @RequestParam String refreshToken) {
        Map<String, String> tokens = authService.refreshToken(refreshToken);
        return R.ok(tokens);
    }
}

查询用户信息服务

java 复制代码
package com.example.demo.service;
import com.example.demo.entity.SysUser;
public interface UserService {
    SysUser selectUserByUserName(String username);
}
java 复制代码
package com.example.demo.service.impl;

import com.example.demo.entity.SysUser;
import com.example.demo.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
    @Override
    public SysUser selectUserByUserName(String username) {
        return null;
    }
}

权限认证服务

java 复制代码
package com.example.demo.service;

import com.example.demo.Vo.LoginResult;
import com.example.demo.entity.LoginUser;
import com.example.demo.entity.SysUser;
import com.example.demo.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.service.spi.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 认证服务
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class AuthService {

    private final AuthenticationManager authenticationManager;
    private final JwtUtil jwtUtil;
    private final TokenService tokenService;
    private final UserService userService;
    private final UserDetailsService userDetailsService;


    /**
     * 登录认证
     */
    @Transactional
    public LoginResult login(String username, String password,
                             String code, String uuid) {
        // 1. 创建认证 Token
        UsernamePasswordAuthenticationToken authToken = 
            new UsernamePasswordAuthenticationToken(username, password);

        // 2. 执行认证 (会调用 UserDetailsService)
        Authentication authenticate;
        try {
            authenticate = authenticationManager.authenticate(authToken);
        } catch (BadCredentialsException e) {
            throw new ServiceException("用户名或密码错误");
        } catch (DisabledException e) {
            throw new ServiceException("账号已被停用");
        } catch (Exception e) {
            log.error("登录认证失败", e);
            throw new ServiceException("登录失败:" + e.getMessage());
        }

        // 3. 获取登录用户
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();

        // 4. 记录登录日志
  //      recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");

        // 5. 生成 Token
        String deviceId = UUID.randomUUID().toString();
        String accessToken = jwtUtil.generateToken(loginUser, deviceId);
        String refreshToken = jwtUtil.generateRefreshToken(username);

        // 6. 将用户信息缓存到 Redis
        tokenService.saveLoginUser(loginUser, accessToken, deviceId);

        // 7. 构建返回结果
        LoginResult result = new LoginResult();
        result.setAccessToken(accessToken);
        result.setRefreshToken(refreshToken);
   //     result.setUserInfo(buildUserInfo(loginUser));

        return result;
    }

    /**
     * 退出登录
     */
    public void logout(String token) {
        // 从 Redis 中删除登录信息
   //     tokenService.deleteLoginUser(token);

        // 记录登出日志
        Claims claims = jwtUtil.parseToken(token);
        if (claims != null) {
            String username = claims.getSubject();
        //    recordLogininfor(username, Constants.LOGOUT, "退出成功");
        }
    }

    /**
     * 刷新 Token
     */
    @Transactional
    public Map<String, String> refreshToken(String refreshToken) {
        // 1. 验证 RefreshToken
        if (!jwtUtil.validateToken(refreshToken)) {
            throw new ServiceException("RefreshToken 已过期");
        }

        // 2. 获取用户名
        Claims claims = jwtUtil.parseToken(refreshToken);
        String username = claims.getSubject();

        // 3. 重新生成 Token
        LoginUser loginUser = loadUserByUsername(username);
        String deviceId = UUID.randomUUID().toString();
        String newAccessToken = jwtUtil.generateToken(loginUser, deviceId);
        String newRefreshToken = jwtUtil.generateRefreshToken(username);

        // 4. 更新 Redis缓存
        tokenService.saveLoginUser(loginUser, newAccessToken, deviceId);

        // 5. 返回新 Token
        Map<String, String> tokens = new HashMap<>();
        tokens.put("accessToken", newAccessToken);
        tokens.put("refreshToken", newRefreshToken);

        return tokens;
    }

    /**
     * 加载用户信息
     */
    private LoginUser loadUserByUsername(String username) {
        SysUser user = userService.selectUserByUserName(username);
        if (user == null) {
            throw new ServiceException("用户不存在");
        }
        return (LoginUser)userDetailsService.loadUserByUsername(username);
    }
}

验证码服务接口设计

java 复制代码
package com.example.demo.service;
public interface CaptchaService {
    void verifyCaptcha(String userName,String captchaCode);
}
java 复制代码
package com.example.demo.service.impl;
import com.example.demo.service.CaptchaService;
import org.springframework.stereotype.Service;
@Service
public class CaptchaServiceImpl implements CaptchaService {

    @Override
    public void verifyCaptcha(String userName,String captchaCode) {

    }
}

springSecurity配置类

java 复制代码
package com.example.demo.config;

import com.example.demo.entity.SysUser;
import com.example.demo.filter.JwtAuthenticationFilter;
import com.example.demo.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.http.SessionCreationPolicy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
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;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * Spring Security 6 配置
 */
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig {

    private final JwtAuthenticationFilter jwtAuthenticationFilter;
 //   private final AuthenticationProvider authenticationProvider;

    private final UserService userService;

    /**
     * 关键步骤:定义 AuthenticationManager Bean
     */
//    @Bean
//    public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
//        // 构建并返回 AuthenticationManager
//        return http.getSharedObject(AuthenticationManager.class);
//        // 或者更常见的做法是直接使用 build() 后的对象,但在 Spring Security 6 中推荐以下方式:
//    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }

    /**
     * 配置安全过滤器链
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            // 禁用 CSRF
            .csrf(csrf -> csrf.disable())

            // 禁用 Session
            .sessionManagement(session -> 
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))

            // 配置授权规则
            .authorizeHttpRequests(auth -> auth
                // 放行静态资源和公开接口
                .requestMatchers("/login", "/register", "/captchaImage")
                    .permitAll()
                .requestMatchers("/common/download/**", "/common/download/resource")
                    .permitAll()
                .requestMatchers("/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**")
                    .permitAll()
                // 其他请求需要认证
                .anyRequest().authenticated()
            )

            // 添加 JWT 过滤器
            .addFilterBefore(jwtAuthenticationFilter, 
                UsernamePasswordAuthenticationFilter.class)

            // 配置异常处理
            .exceptionHandling(exceptions -> exceptions
                .authenticationEntryPoint((request, response, authException) -> {
                    // 未认证处理
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().write("{\"code\":401,\"msg\":\"未认证\"}");
                })
                .accessDeniedHandler((request, response, accessDeniedException) -> {
                    // 无权限处理
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().write("{\"code\":403,\"msg\":\"无权限\"}");
                })
            );

        return http.build();
    }

    /**
     * 配置认证提供者
     */
    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(userDetailsService());
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

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

    /**
     * 用户详情服务
     */
    @Bean
    public UserDetailsService userDetailsService() {
        return username -> {
            // 从数据库加载用户信息
            SysUser user = userService.selectUserByUserName(username);
            if (user == null) {
                throw new UsernameNotFoundException("用户不存在");
            }
            return new org.springframework.security.core.userdetails.User(
                username,
                user.getPassword(),
                getAuthorities(user)
            );
        };
    }

    /**
     * 获取用户权限列表
     */
    private Collection<? extends GrantedAuthority> getAuthorities(SysUser user) {
        List<GrantedAuthority> authorities = new ArrayList<>();

//        // 添加角色权限
//        for (SysRole role : user.getRoles()) {
//            authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleKey()));
//        }
//
//        // 添加操作权限
//        for (String permission : user.getPermissions()) {
//            authorities.add(new SimpleGrantedAuthority(permission));
//        }

        return authorities;
    }
}

jwt配置

java 复制代码
package com.example.demo.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

/**
 * JWT配置类,读取Application.yml中的配置
 * 
 * @author AdminMall
 *
 */
@Setter
@Getter
@Configuration
public class JwtConfig {
	@Value("${jwt.tokenHeader}")
	private String tokenHeader; // JWT存储的请求头
	@Value("${jwt.secret}")
	private String secret; // jwt加解密使用的密钥
	@Value("${jwt.expiration}")
	private long expiration; // JWT的超时时间
	@Value("${jwt.tokenHead}")
	private String tokenHead; // JWT负载中拿到的开头
	@Value("${jwt.refreshExpiration}")
	private Long refreshExpiration; // RefreshToken 过期时间 (7 天)

}

jwt生成工具类

java 复制代码
package com.example.demo.utils;

import com.example.demo.config.JwtConfig;
import com.example.demo.entity.LoginUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * JWT Token 工具类
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtUtil {
    @Autowired
    private JwtConfig jwtConfig;

    /**
     * 生成 Token
     */
    public String generateToken(LoginUser loginUser, String deviceId) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", loginUser.getUserId());
        claims.put("userName", loginUser.getUsername());
        claims.put("deviceId", deviceId);

        Date now = new Date();
        Date expireDate = new Date(now.getTime() + jwtConfig.getExpiration());

        return Jwts.builder()
            .setClaims(claims)
            .setId(UUID.randomUUID().toString())
            .setIssuedAt(now)
            .setExpiration(expireDate)
            .signWith(SignatureAlgorithm.HS256, jwtConfig.getSecret())
            .compact();
    }

    /**
     * 生成 RefreshToken
     */
    public String generateRefreshToken(String username) {
        Date now = new Date();
        Date expireDate = new Date(now.getTime() + jwtConfig.getRefreshExpiration());

        return Jwts.builder()
            .setSubject(username)
            .setId(UUID.randomUUID().toString())
            .setIssuedAt(now)
            .setExpiration(expireDate)
            .signWith(SignatureAlgorithm.HS256, jwtConfig.getSecret())
            .compact();
    }

    /**
     * 解析 Token
     */
    public Claims parseToken(String token) {
        try {
            return Jwts.parser()
                .setSigningKey(jwtConfig.getSecret())
                .parseClaimsJws(token)
                .getBody();
        } catch (Exception e) {
            log.error("解析 Token 失败:{}", e.getMessage());
            return null;
        }
    }

    /**
     * 从 Token 中获取用户 ID
     */
    public Long getUserId(String token) {
        Claims claims = parseToken(token);
        if (claims == null) {
            return null;
        }
        return Long.valueOf(claims.get("userId").toString());
    }

    /**
     * 验证 Token 是否过期
     */
    public boolean isTokenExpired(String token) {
        Claims claims = parseToken(token);
        if (claims == null) {
            return true;
        }
        return claims.getExpiration().before(new Date());
    }

    /**
     * 验证 Token 是否有效
     */
    public boolean validateToken(String token) {
        try {
            Claims claims = parseToken(token);
            return claims != null && !isTokenExpired(token);
        } catch (Exception e) {
            log.error("验证 Token 失败", e);
            return false;
        }
    }
}

分布式token存redis共享服务

java 复制代码
package com.example.demo.service;

import com.example.demo.entity.LoginUser;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * Token 服务优化版
 */
@Service
@RequiredArgsConstructor
public class TokenService {

   // private final RedisUtil redisUtil;

    /**
     * 保存登录用户 (带缓存预热)
     */
    public void saveLoginUser(LoginUser user, String token, String deviceId) {
        String userKey = "login:token:" + token;
        String deviceKey = "login:device:" + deviceId;

        // 1. 缓存用户信息
     //   redisUtil.set(userKey, user, 2, TimeUnit.HOURS);

        // 2. 建立 Token 和设备映射
     //   redisUtil.set(deviceKey, token, 2, TimeUnit.HOURS);

        // 3. 预热用户权限数据
        warmUpUserPermissions(user.getUserId());
    }

    /**
     * 获取登录用户 (多级缓存)
     */
    public LoginUser getLoginUser(String token) {
        // 1. 检查 Token 黑名单
//        if (isTokenBlacklisted(token)) {
//            return null;
//        }

        // 2. 从 Redis 获取
        String userKey = "login:token:" + token;
    //    LoginUser user = redisUtil.get(userKey, LoginUser.class);

//        if (user != null) {
//            // 3. 续期
//            redisUtil.expire(userKey, 2, TimeUnit.HOURS);
//        }

        return null;
    }

    /**
     * 预热用户权限数据
     */
    private void warmUpUserPermissions(Long userId) {
        // 提前加载用户权限到缓存
        String permKey = "user:perms:" + userId;
        // ... 加载权限数据
    }

    /**
     * 检查 Token 是否在黑名单中
//     */
//    private boolean isTokenBlacklisted(String token) {
//        return Boolean.TRUE.equals(redisUtil.hasKey("token:blacklist:" + token));
//    }
}

userDetailsService服务,springSecurity框架提供

java 复制代码
/*
 * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.security.core.userdetails;

/**
 * Core interface which loads user-specific data.
 * <p>
 * It is used throughout the framework as a user DAO and is the strategy used by the
 * {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider
 * DaoAuthenticationProvider}.
 *
 * <p>
 * The interface requires only one read-only method, which simplifies support for new
 * data-access strategies.
 *
 * @author Ben Alex
 * @see org.springframework.security.authentication.dao.DaoAuthenticationProvider
 * @see UserDetails
 */
public interface UserDetailsService {

	/**
	 * Locates the user based on the username. In the actual implementation, the search
	 * may possibly be case sensitive, or case insensitive depending on how the
	 * implementation instance is configured. In this case, the <code>UserDetails</code>
	 * object that comes back may have a username that is of a different case than what
	 * was actually requested..
	 * @param username the username identifying the user whose data is required.
	 * @return a fully populated user record (never <code>null</code>)
	 * @throws UsernameNotFoundException if the user could not be found or the user has no
	 * GrantedAuthority
	 */
	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

}

jwt过滤器

java 复制代码
package com.example.demo.filter;

import com.example.demo.entity.LoginUser;
import com.example.demo.service.TokenService;
import com.example.demo.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import io.micrometer.common.util.StringUtils;
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.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
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;

/**
 * JWT 认证过滤器
 */
@Slf4j
@Component
@RequiredArgsConstructor
@Lazy
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Autowired
    private final JwtUtil jwtUtil;
    @Autowired
    private final TokenService tokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {

        // 1. 获取 Token
        String token = getTokenFromRequest(request);

        // 2. 没有 Token,直接放行
        if (StringUtils.isEmpty(token)) {
            filterChain.doFilter(request, response);
            return;
        }

        // 3. 验证 Token
        if (!jwtUtil.validateToken(token)) {
            log.warn("Token 无效或已过期");
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\":401,\"msg\":\"Token 无效或已过期\"}");
            return;
        }

        // 4. 解析 Token,获取用户信息
        Claims claims = jwtUtil.parseToken(token);
        Long userId = Long.valueOf(claims.get("userId").toString());
        String username = claims.getSubject();

        // 5. 从 Redis 中获取登录用户信息
        LoginUser loginUser = tokenService.getLoginUser(token);
        if (loginUser == null) {
            log.warn("登录用户不存在");
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            response.getWriter().write("{\"code\":401,\"msg\":\"登录用户不存在\"}");
            return;
        }

        // 6. 设置 Spring Security 上下文
        UsernamePasswordAuthenticationToken authenticationToken = 
            new UsernamePasswordAuthenticationToken(loginUser, null, 
                loginUser.getAuthorities());
        authenticationToken.setDetails(new WebAuthenticationDetailsSource()
            .buildDetails(request));

        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        log.debug("用户 [{}] 认证成功", username);

        // 7. 继续过滤链
        filterChain.doFilter(request, response);
    }

    /**
     * 从请求中获取 Token
     */
    private String getTokenFromRequest(HttpServletRequest request) {
        // 从 Header 中获取
        String token = request.getHeader("Authorization");

        // 支持 Bearer Token 格式
        if (StringUtils.isNotEmpty(token) && token.startsWith("Bearer ")) {
            token = token.substring(7);
        }

        // 如果 Header 中没有,尝试从参数中获取
        if (StringUtils.isEmpty(token)) {
            token = request.getParameter("token");
        }

        return token;
    }
}

token防止篡改

java 复制代码
package com.example.demo.utils;

import com.example.demo.entity.LoginUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;

import java.util.Map;

/**
 * Token 签名验证增强
 */
@Slf4j
public class SecureJwtUtil extends JwtUtil {

    @Value("${jwt.secret}")
    private String secret;

    /**
     * 双重签名验证
     */
    public boolean validateTokenSecure(String token) {
        // 1. 基础验证
        if (!validateToken(token)) {
            return false;
        }

        // 2. 检查签名算法
        Claims claims = parseToken(token);
        String alg = (String) claims.get("alg");
        if (!"HS256".equals(alg)) {
            log.warn("不支持的签名算法:{}", alg);
            return false;
        }

        // 3. 检查必要字段
        if (claims.get("userId") == null || claims.getSubject() == null) {
            log.warn("Token 缺少必要字段");
            return false;
        }

        return true;
    }

    /**
     * 添加指纹验证
     */
    public String generateTokenWithFingerprint(LoginUser user, String fingerprint) {
        Map<String, Object> claims = buildClaims(user);
        claims.put("fingerprint", fingerprint); // 设备指纹

        return Jwts.builder()
            .setClaims(claims)
            .signWith(SignatureAlgorithm.HS256, secret)
            .compact();
    }
}

yaml配置

java 复制代码
jwt:
  tokenHeader: Authorization #JWT存储的请求头
  secret: mall-jwt-test #jwt加解密使用的密钥
  expiration: 604800 #JWT的超时时间
  tokenHead: Bearer #JWT负载中拿到的开头
  refreshExpiration: 604800

代码写道这里基本上大体上就完成了一个简单的demo搭建,大家可以照着试一试,学习永不止步

相关推荐
想吃火锅10051 小时前
【前端手撕】promise.all
前端
lichenyang4531 小时前
动态加载 vs 延迟加载:为什么 demo 里「延迟」看起来没效果?
前端
cypking1 小时前
从零搭建 Claude Code + Chrome MCP 浏览器自动化:前端 E2E 端到端测试完整教程(包含增量测试)
前端·chrome·自动化
SXJR1 小时前
spring boot + langchain4j +milvus实现向量存储
java·spring boot·后端·大模型·milvus·rag·langchain4j
武子康2 小时前
Java-27 深入浅出 Spring - 实现简易Ioc-03 在上节的业务下手动实现IoC 从 XML 配置到 BeanFactory 反射注入
java·后端·mybatis
二哈赛车手2 小时前
新人笔记---idea索引失效问题解决方案
java·笔记·spring·elasticsearch·intellij-idea
Levi_J2 小时前
Vue2 升级 Vue3 项目实战
前端
前端拷贝猿2 小时前
扫码领券功能需求分析
前端
前端拷贝猿2 小时前
设备活动弹窗功能需求分析
前端