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 测试登录接口
-
先调用addUser方法新增测试用户(如username=admin,password=123456,role=admin),密码会自动加密存储;
2. 用Postman调用POST接口:http://localhost:8080/auth/login,请求体如下:
{
"username": "admin",
"password": "123456"
}
- 响应结果(成功):返回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 MVC | Servlet |
| 执行时机 | 请求进入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接口,正常返回接口数据,校验通过。
六、常见问题与解决方案
-
令牌验证失败,提示“签名错误”?
解决方案:① 检查JwtUtil中使用的密钥(secret)是否与配置文件一致;② 确认前端携带的令牌是否完整,无多余空格或字符;③ 检查令牌生成时的签名算法(本文用HS256)与验证时是否一致。 -
拦截器/过滤器未生效?
解决方案:① 检查拦截器/过滤器是否添加@Component注解(注入Spring容器);② 检查注册配置类(WebMvcConfig/FilterConfig)是否添加@Configuration注解;③ 确认拦截规则是否正确,避免放行接口写错。 -
前端携带令牌后,后端仍提示“未携带令牌”?
解决方案:① 检查前端请求头字段名是否与配置文件中jwt.header一致(默认Authorization);② 确认令牌格式是否正确(Bearer + 空格 + 令牌);③ 排查跨域问题(若前后端分离,需配置跨域允许携带请求头)。 -
密码加密后,登录时校验失败?
解决方案:① 确认新增用户时,是否使用BCryptPasswordEncoder加密密码;② 登录校验时,是否使用matches方法(明文密码与加密密码匹配),而非直接equals比较。
七、总结
本文完整实现了Spring Boot接口权限控制的核心流程:从用户登录接口设计、JWT令牌生成与验证,到拦截器/过滤器实现权限校验,完美解决“接口无安全控制”的问题,提升项目专业度。核心要点:
-
登录接口:核心是用户校验(用户名、密码、状态),密码必须加密存储,避免安全风险。
-
JWT工具类:封装生成、验证、解析令牌的核心方法,密钥和过期时间需配置化,便于维护。
-
权限校验:优先使用拦截器(Spring Boot适配更好),按需配置拦截/放行规则,支持角色权限区分。
掌握本文内容后,可直接将代码整合到毕设或实际项目中,实现接口的安全保护。后续可进一步扩展:如令牌刷新机制、更细粒度的权限控制(基于接口的权限分配)、异常统一处理等,让权限体系更完善。
2700

被折叠的 条评论
为什么被折叠?



