Spring Boot 接口权限控制:登录 + JWT 实战

Spring Boot 接口权限控制:登录 + JWT 实战

在Spring Boot项目开发中,完成接口开发只是基础,接口的安全控制才是提升项目专业度的关键——如果接口没有权限校验,任何人都能随意调用接口、操作数据,会造成严重的安全隐患。本文承接“接口开发完成后无安全控制”的场景,手把手教大家实现用户登录接口、整合JWT完成令牌生成与验证,以及通过拦截器/过滤器实现接口权限校验,完整落地接口安全体系,适配实际项目与毕设需求,快速提升开发专业度。

一、前置准备:环境搭建与核心依赖

本次实战基于Spring Boot + MyBatis-Plus + JWT + Spring Security(简化版),核心依赖如下,无需额外引入过多复杂组件,兼顾轻量与实用。

1.1 Maven 核心依赖

<!-- Spring Boot Web 核心依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- MyBatis-Plus 核心依赖(操作数据库) -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>

<!-- MySQL 驱动 -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

<!-- JWT 核心依赖(生成/验证令牌) -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

<!-- lombok 简化实体类 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

<!-- Spring Security 简化版(可选,用于权限注解) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

1.2 核心配置(application.yml)

配置数据库连接、JWT核心参数(密钥、过期时间)、MyBatis-Plus相关配置,集中管理,便于后续修改:

spring:
  # 数据库配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot_jwt?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
    username: root
    password: 123456
  # Spring Security 配置(关闭默认登录页,简化配置)
  security:
    basic:
      enabled: false
    user:
      name: admin
      password: admin

# MyBatis-Plus 配置
mybatis-plus:
  mapper-locations: classpath:mybatis/mapper/**/*.xml
  type-aliases-package: com.example.demo.entity
  configuration:
    map-underscore-to-camel-case: true # 驼峰命名映射
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL日志(便于调试)

# JWT 自定义配置(自己定义,后续代码中读取)
jwt:
  secret: springboot-jwt-2026-security-key # 密钥(生产环境需更换为复杂密钥,避免泄露)
  expiration: 86400000 # 令牌过期时间(单位:毫秒),这里设置为24小时
  header: Authorization # 请求头中携带令牌的字段名,默认Authorization

1.3 数据库表设计(用户表)

核心是用户表,存储用户名、密码(加密存储)、角色(用于后续权限区分,简化版用字符串表示),无需复杂设计,适配登录与权限校验核心需求:

CREATE TABLE `sys_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户主键',
  `username` varchar(50) NOT NULL COMMENT '用户名(唯一)',
  `password` varchar(100) NOT NULL COMMENT '密码(加密存储)',
  `role` varchar(20) NOT NULL COMMENT '用户角色(如:admin、user)',
  `status` tinyint(1) DEFAULT 1 COMMENT '状态(1:正常,0:禁用)',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户表';

说明:密码需加密存储(本文使用BCrypt加密,Spring Security自带,无需额外引入工具),禁止明文存储,避免安全风险。

二、核心实战一:用户登录接口设计

登录接口是权限控制的入口,核心逻辑:接收前端传入的用户名/密码 → 校验用户名是否存在、密码是否匹配 → 校验通过生成JWT令牌 → 返回令牌给前端;校验失败返回错误信息。

2.1 实体类与DTO设计

2.1.1 用户实体类(SysUser)
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;

/**
 * 系统用户实体类
 */
@Data
@TableName("sys_user")
public class SysUser {
    @TableId(type = IdType.AUTO)
    private Long id;

    // 用户名(唯一)
    private String username;

    // 加密后的密码
    private String password;

    // 用户角色(admin:管理员,user:普通用户)
    private String role;

    // 状态(1:正常,0:禁用)
    private Integer status;

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
}
2.1.2 登录请求DTO(LoginDTO)

接收前端传入的登录参数,用于参数校验(用户名、密码非空):

import lombok.Data;
import javax.validation.constraints.NotBlank;

/**
 * 登录请求DTO(接收前端参数)
 */
@Data
public class LoginDTO {
    // 用户名,非空校验
    @NotBlank(message = "用户名不能为空")
    private String username;

    // 密码,非空校验
    @NotBlank(message = "密码不能为空")
    private String password;
}
2.1.3 登录响应DTO(LoginVO)

返回给前端的结果,包含JWT令牌、用户名、角色等信息,避免返回敏感数据(如加密后的密码):

import lombok.Data;

/**
 * 登录响应VO(返回给前端)
 */
@Data
public class LoginVO {
    // JWT令牌
    private String token;

    // 用户名
    private String username;

    // 用户角色
    private String role;

    // 令牌过期时间(单位:毫秒)
    private Long expiration;
}

2.2 Mapper、Service层实现(用户校验)

2.2.1 SysUserMapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.SysUser;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

/**
 * 用户Mapper接口
 */
@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
    // 根据用户名查询用户(登录核心查询)
    SysUser selectByUsername(@Param("username") String username);
}
2.2.2 SysUserService

核心是用户校验逻辑:查询用户、校验密码、校验用户状态,使用Spring Security的BCryptPasswordEncoder进行密码加密与匹配:

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.SysUser;
import com.example.demo.mapper.SysUserMapper;
import com.example.demo.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

/**
 * 用户Service层
 */
@Service
public class SysUserService extends ServiceImpl<SysUserMapper, SysUser> {

    @Autowired
    private SysUserMapper sysUserMapper;

    // BCrypt密码加密器(Spring Security自带)
    private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    /**
     * 登录校验:根据用户名和密码校验用户
     * @param username 用户名
     * @param password 明文密码
     * @return 校验通过的用户信息
     */
    public SysUser loginCheck(String username, String password) {
        // 1. 根据用户名查询用户
        SysUser user = sysUserMapper.selectByUsername(username);
        Assert.notNull(user, "用户名不存在");

        // 2. 校验用户状态(是否禁用)
        Assert.isTrue(user.getStatus() == 1, "用户已禁用,请联系管理员");

        // 3. 校验密码(明文密码与数据库加密密码匹配)
        Assert.isTrue(passwordEncoder.matches(password, user.getPassword()), "密码错误");

        // 4. 校验通过,返回用户信息(注意:不要返回密码)
        user.setPassword(null);
        return user;
    }

    /**
     * 新增用户(用于测试,密码自动加密)
     * @param user 用户信息
     * @return 新增结果
     */
    public boolean addUser(SysUser user) {
        // 密码加密后存储
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        return save(user);
    }
}

2.3 Controller层:登录接口实现

接收前端请求,调用Service层校验用户,校验通过后调用JWT工具类生成令牌,返回登录结果:

import com.example.demo.dto.LoginDTO;
import com.example.demo.dto.LoginVO;
import com.example.demo.entity.SysUser;
import com.example.demo.service.SysUserService;
import com.example.demo.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

/**
 * 登录控制器
 */
@RestController
@RequestMapping("/auth")
@Validated
public class LoginController {

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private JwtUtil jwtUtil;

    // 从配置文件读取JWT过期时间
    @Value("${jwt.expiration}")
    private Long expiration;

    /**
     * 用户登录接口
     * @param loginDTO 登录参数(用户名、密码)
     * @return 登录结果(JWT令牌等)
     */
    @PostMapping("/login")
    public LoginVO login(@Valid @RequestBody LoginDTO loginDTO) {
        // 1. 登录校验(用户名、密码、用户状态)
        SysUser user = sysUserService.loginCheck(loginDTO.getUsername(), loginDTO.getPassword());

        // 2. 生成JWT令牌(传入用户ID、用户名、角色)
        String token = jwtUtil.generateToken(user.getId(), user.getUsername(), user.getRole());

        // 3. 组装返回结果
        LoginVO loginVO = new LoginVO();
        loginVO.setToken(token);
        loginVO.setUsername(user.getUsername());
        loginVO.setRole(user.getRole());
        loginVO.setExpiration(expiration);

        return loginVO;
    }
}

2.4 测试登录接口

  1. 先调用addUser方法新增测试用户(如username=admin,password=123456,role=admin),密码会自动加密存储;

    2. 用Postman调用POST接口:http://localhost:8080/auth/login,请求体如下:
    
{
  "username": "admin",
  "password": "123456"
}
  1. 响应结果(成功):返回token、username、role、expiration,前端需存储token,后续请求接口时携带该token。

三、核心实战二:JWT 生成与验证(工具类实现)

JWT(JSON Web Token)是一种无状态的令牌机制,核心优势是无需在服务器存储会话信息,仅通过令牌本身即可完成身份验证,适合分布式系统。本文封装JWT工具类,实现令牌生成、验证、解析核心功能。

3.1 JWT工具类(JwtUtil)

核心逻辑:读取配置文件中的密钥和过期时间,使用jjwt依赖提供的API生成令牌、验证令牌有效性、解析令牌中的用户信息(用户ID、用户名、角色)。

import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;

/**
 * JWT工具类:生成令牌、验证令牌、解析令牌
 */
@Component
public class JwtUtil {

    // 从配置文件读取JWT密钥
    @Value("${jwt.secret}")
    private String secret;

    // 从配置文件读取令牌过期时间(毫秒)
    @Value("${jwt.expiration}")
    private Long expiration;

    // 令牌前缀(规范写法,前端请求头携带时需加上)
    private static final String TOKEN_PREFIX = "Bearer ";

    /**
     * 生成JWT令牌
     * @param userId 用户ID
     * @param username 用户名
     * @param role 用户角色
     * @return JWT令牌(不带前缀)
     */
    public String generateToken(Long userId, String username, String role) {
        // 令牌过期时间 = 当前时间 + 配置的过期时间
        Date expirationDate = new Date(System.currentTimeMillis() + expiration);

        // 构建JWT令牌
        return Jwts.builder()
                // 载荷:存储用户核心信息(自定义字段)
                .claim("userId", userId)
                .claim("username", username)
                .claim("role", role)
                // 主题(可自定义,一般存储用户名)
                .setSubject(username)
                // 签发时间
                .setIssuedAt(new Date())
                // 过期时间
                .setExpiration(expirationDate)
                // 签名算法:HS256,使用配置的密钥
                .signWith(SignatureAlgorithm.HS256, secret)
                // 压缩令牌
                .compact();
    }

    /**
     * 验证JWT令牌有效性
     * @param token 待验证的令牌(可带前缀,也可不带)
     * @return true:有效,false:无效
     */
    public boolean validateToken(String token) {
        try {
            // 去除令牌前缀(如果有)
            token = token.replace(TOKEN_PREFIX, "");
            // 解析令牌,验证签名和过期时间
            Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            // 捕获所有异常(签名错误、令牌过期、格式错误等)
            return false;
        }
    }

    /**
     * 解析JWT令牌,获取载荷中的用户信息(通用方法)
     * @param token 令牌(可带前缀)
     * @return 载荷对象(包含自定义的userId、username、role)
     */
    public Claims parseToken(String token) {
        token = token.replace(TOKEN_PREFIX, "");
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * 从令牌中获取用户名
     * @param token 令牌
     * @return 用户名
     */
    public String getUsernameFromToken(String token) {
        Claims claims = parseToken(token);
        return claims.get("username", String.class);
    }

    /**
     * 从令牌中获取用户角色
     * @param token 令牌
     * @return 用户角色
     */
    public String getRoleFromToken(String token) {
        Claims claims = parseToken(token);
        return claims.get("role", String.class);
    }

    /**
     * 从令牌中获取用户ID
     * @param token 令牌
     * @return 用户ID
     */
    public Long getUserIdFromToken(String token) {
        Claims claims = parseToken(token);
        return claims.get("userId", Long.class);
    }
}

3.2 JWT核心注意事项

1. 密钥(secret):生产环境必须更换为复杂的随机字符串(如32位随机字符),避免泄露,否则会导致令牌被伪造;

2. 过期时间(expiration):根据业务需求设置,一般为1-24小时,过期后前端需重新登录获取新令牌;

3. 载荷信息:仅存储非敏感核心信息(如用户ID、用户名、角色),禁止存储密码、手机号等敏感数据;

4. 令牌前缀:前端请求时,需在请求头Authorization中携带令牌,格式为:Bearer + 空格 + 令牌(如:Bearer eyJhbGciOiJIUzI1NiJ9...)。

四、核心实战三:拦截器 / 过滤器实现接口权限校验

生成JWT令牌后,需要拦截所有需要权限的接口,校验令牌的有效性、用户的权限,实现接口保护。本文分别实现两种方式:拦截器(HandlerInterceptor)和过滤器(Filter),按需选择,核心逻辑一致。

方式一:拦截器(HandlerInterceptor)实现权限校验

拦截器是Spring MVC提供的组件,可拦截请求的预处理、后处理,适合与Spring生态整合,便于获取Spring容器中的Bean(如JwtUtil),推荐使用。

4.1.1 自定义拦截器(JwtInterceptor)

核心逻辑:拦截请求 → 从请求头获取令牌 → 验证令牌有效性 → 解析用户信息 → 校验用户权限(可选) → 放行;若令牌无效/无令牌,直接返回401未授权。

import com.example.demo.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

/**
 * JWT拦截器:实现接口权限校验
 */
@Component
public class JwtInterceptor implements HandlerInterceptor {

    @Autowired
    private JwtUtil jwtUtil;

    // 从配置文件读取请求头中令牌的字段名
    @Value("${jwt.header}")
    private String jwtHeader;

    /**
     * 请求预处理(核心方法):在接口执行前拦截,校验令牌
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1. 从请求头中获取令牌
        String token = request.getHeader(jwtHeader);

        // 2. 校验令牌是否存在
        if (token == null || token.trim().isEmpty()) {
            responseError(response, "未携带令牌,请先登录");
            return false;
        }

        // 3. 验证令牌有效性
        if (!jwtUtil.validateToken(token)) {
            responseError(response, "令牌无效或已过期,请重新登录");
            return false;
        }

        // 4. (可选)权限校验:根据业务需求,校验用户角色是否有权限访问当前接口
        // 示例:假设/admin/**接口只有admin角色可访问
        String requestUri = request.getRequestURI();
        String role = jwtUtil.getRoleFromToken(token);
        if (requestUri.startsWith("/admin/") && !"admin".equals(role)) {
            responseError(response, "权限不足,无法访问");
            return false;
        }

        // 5. 令牌校验通过,放行请求
        return true;
    }

    /**
     * 响应错误信息(统一返回JSON格式)
     */
    private void responseError(HttpServletResponse response, String message) throws Exception {
        response.setContentType("application/json;charset=utf-8");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 401未授权
        PrintWriter out = response.getWriter();
        out.write("{\"code\":401,\"message\":\"" + message + "\"}");
        out.flush();
        out.close();
    }
}
4.1.2 注册拦截器(配置拦截规则)

通过WebMvcConfigurer配置拦截器,指定拦截哪些接口、放行哪些接口(如登录接口无需拦截):

import com.example.demo.interceptor.JwtInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * Spring MVC 配置:注册JWT拦截器
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private JwtInterceptor jwtInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor)
                // 拦截所有接口
                .addPathPatterns("/**")
                // 放行接口(无需权限校验)
                .excludePathPatterns(
                        "/auth/login", // 登录接口
                        "/auth/register", // 注册接口(如果有)
                        "/swagger-ui/**", // Swagger文档(开发环境放行)
                        "/v3/api-docs/**" // Swagger接口(开发环境放行)
                );
    }
}

方式二:过滤器(Filter)实现权限校验

过滤器是Servlet提供的组件,作用于请求进入Servlet之前,更底层,适合通用的权限校验场景。实现逻辑与拦截器一致,仅代码写法不同。

4.2.1 自定义过滤器(JwtFilter)
import com.example.demo.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * JWT过滤器:实现接口权限校验
 */
@Component
public class JwtFilter implements Filter {

    @Autowired
    private JwtUtil jwtUtil;

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

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        // 1. 获取令牌
        String token = request.getHeader(jwtHeader);

        // 2. 校验令牌是否存在
        if (token == null || token.trim().isEmpty()) {
            responseError(response, "未携带令牌,请先登录");
            return;
        }

        // 3. 验证令牌有效性
        if (!jwtUtil.validateToken(token)) {
            responseError(response, "令牌无效或已过期,请重新登录");
            return;
        }

        // 4. (可选)权限校验(与拦截器逻辑一致)
        String requestUri = request.getRequestURI();
        String role = jwtUtil.getRoleFromToken(token);
        if (requestUri.startsWith("/admin/") && !"admin".equals(role)) {
            responseError(response, "权限不足,无法访问");
            return;
        }

        // 5. 放行请求
        filterChain.doFilter(request, response);
    }

    // 响应错误信息(与拦截器一致)
    private void responseError(HttpServletResponse response, String message) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        PrintWriter out = response.getWriter();
        out.write("{\"code\":401,\"message\":\"" + message + "\"}");
        out.flush();
        out.close();
    }
}
4.2.2 注册过滤器

通过FilterRegistrationBean注册过滤器,指定拦截规则,与拦截器的放行/拦截逻辑一致:

import com.example.demo.filter.JwtFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 过滤器配置:注册JWT过滤器
 */
@Configuration
public class FilterConfig {

    @Autowired
    private JwtFilter jwtFilter;

    @Bean
    public FilterRegistrationBean<JwtFilter> jwtFilterRegistration() {
        FilterRegistrationBean<JwtFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(jwtFilter);
        // 拦截所有接口
        registrationBean.addUrlPatterns("/*");
        // 放行接口(与拦截器一致)
        registrationBean.addInitParameter("excludedPaths", "/auth/login,/auth/register,/swagger-ui/**,/v3/api-docs/**");
        registrationBean.setName("JwtFilter");
        registrationBean.setOrder(1); // 过滤器执行顺序,越小越先执行
        return registrationBean;
    }
}

拦截器与过滤器对比(选型建议)

对比维度拦截器(HandlerInterceptor)过滤器(Filter)
所属框架Spring MVCServlet
执行时机请求进入Controller之前请求进入Servlet之前(更底层)
依赖Spring是,可直接注入Spring Bean(如JwtUtil)否,需通过配置注入Bean,稍繁琐
适用场景Spring Boot项目,需要结合Spring生态(如权限注解)通用Java Web项目,无需依赖Spring
选型建议:Spring Boot项目优先使用拦截器,整合更顺畅,代码更简洁;非Spring项目使用过滤器。

五、实战测试:接口权限校验效果

5.1 测试场景1:未携带令牌访问需要权限的接口

调用接口:http://localhost:8080/admin/user/list(假设是管理员接口),未在请求头携带Authorization令牌,响应结果:

{
  "code": 401,
  "message": "未携带令牌,请先登录"
}

5.2 测试场景2:携带无效令牌访问接口

请求头携带错误令牌(如随意输入字符串),响应结果:

{
  "code": 401,
  "message": "令牌无效或已过期,请重新登录"
}

5.3 测试场景3:普通用户访问管理员接口

使用role=user的用户登录,获取令牌,携带令牌访问/admin/user/list接口,响应结果:

{
  "code": 401,
  "message": "权限不足,无法访问"
}

5.4 测试场景4:携带有效令牌访问接口

使用role=admin的用户登录,获取令牌,在请求头添加:Authorization: Bearer 令牌,访问/admin/user/list接口,正常返回接口数据,校验通过。

六、常见问题与解决方案

  1. 令牌验证失败,提示“签名错误”?

    解决方案:① 检查JwtUtil中使用的密钥(secret)是否与配置文件一致;② 确认前端携带的令牌是否完整,无多余空格或字符;③ 检查令牌生成时的签名算法(本文用HS256)与验证时是否一致。
    
  2. 拦截器/过滤器未生效?

    解决方案:① 检查拦截器/过滤器是否添加@Component注解(注入Spring容器);② 检查注册配置类(WebMvcConfig/FilterConfig)是否添加@Configuration注解;③ 确认拦截规则是否正确,避免放行接口写错。
    
  3. 前端携带令牌后,后端仍提示“未携带令牌”?

    解决方案:① 检查前端请求头字段名是否与配置文件中jwt.header一致(默认Authorization);② 确认令牌格式是否正确(Bearer + 空格 + 令牌);③ 排查跨域问题(若前后端分离,需配置跨域允许携带请求头)。
    
  4. 密码加密后,登录时校验失败?

    解决方案:① 确认新增用户时,是否使用BCryptPasswordEncoder加密密码;② 登录校验时,是否使用matches方法(明文密码与加密密码匹配),而非直接equals比较。
    

七、总结

本文完整实现了Spring Boot接口权限控制的核心流程:从用户登录接口设计、JWT令牌生成与验证,到拦截器/过滤器实现权限校验,完美解决“接口无安全控制”的问题,提升项目专业度。核心要点:

  • 登录接口:核心是用户校验(用户名、密码、状态),密码必须加密存储,避免安全风险。

  • JWT工具类:封装生成、验证、解析令牌的核心方法,密钥和过期时间需配置化,便于维护。

  • 权限校验:优先使用拦截器(Spring Boot适配更好),按需配置拦截/放行规则,支持角色权限区分。

掌握本文内容后,可直接将代码整合到毕设或实际项目中,实现接口的安全保护。后续可进一步扩展:如令牌刷新机制、更细粒度的权限控制(基于接口的权限分配)、异常统一处理等,让权限体系更完善。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码客日记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值