最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring?Boot項(xiàng)目開發(fā)接口權(quán)限控制之登錄+JWT實(shí)戰(zhàn)指南

 更新時(shí)間:2026年07月24日 08:57:44   作者:碼客日記  
對(duì)于前后端分離的項(xiàng)目,后端對(duì)于接口訪問的權(quán)限控制是必須要做的,也就是需要根據(jù)用戶的權(quán)限進(jìn)行控制,這樣才能對(duì)我們的接口資源進(jìn)行一定程度的保護(hù),這篇文章主要介紹了Spring?Boot項(xiàng)目開發(fā)接口權(quán)限控制之登錄+JWT實(shí)戰(zhàn)指南的相關(guān)資料,需要的朋友可以參考下

前言

在Spring Boot項(xiàng)目開發(fā)中,完成接口開發(fā)只是基礎(chǔ),接口的安全控制才是提升項(xiàng)目專業(yè)度的關(guān)鍵——如果接口沒有權(quán)限校驗(yàn),任何人都能隨意調(diào)用接口、操作數(shù)據(jù),會(huì)造成嚴(yán)重的安全隱患。本文承接“接口開發(fā)完成后無安全控制”的場景,手把手教大家實(shí)現(xiàn)用戶登錄接口、整合JWT完成令牌生成與驗(yàn)證,以及通過攔截器/過濾器實(shí)現(xiàn)接口權(quán)限校驗(yàn),完整落地接口安全體系,適配實(shí)際項(xiàng)目與畢設(shè)需求,快速提升開發(fā)專業(yè)度。

一、前置準(zhǔn)備:環(huán)境搭建與核心依賴

本次實(shí)戰(zhàn)基于Spring Boot + MyBatis-Plus + JWT + Spring Security(簡化版),核心依賴如下,無需額外引入過多復(fù)雜組件,兼顧輕量與實(shí)用。

1.1 Maven 核心依賴

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

<!-- MyBatis-Plus 核心依賴(操作數(shù)據(jù)庫) -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>

<!-- MySQL 驅(qū)動(dòng) -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

<!-- JWT 核心依賴(生成/驗(yàn)證令牌) -->
<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 簡化實(shí)體類 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

<!-- Spring Security 簡化版(可選,用于權(quán)限注解) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

1.2 核心配置(application.yml)

配置數(shù)據(jù)庫連接、JWT核心參數(shù)(密鑰、過期時(shí)間)、MyBatis-Plus相關(guān)配置,集中管理,便于后續(xù)修改:

spring:
  # 數(shù)據(jù)庫配置
  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 配置(關(guān)閉默認(rèn)登錄頁,簡化配置)
  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日志(便于調(diào)試)

# JWT 自定義配置(自己定義,后續(xù)代碼中讀取)
jwt:
  secret: springboot-jwt-2026-security-key # 密鑰(生產(chǎn)環(huán)境需更換為復(fù)雜密鑰,避免泄露)
  expiration: 86400000 # 令牌過期時(shí)間(單位:毫秒),這里設(shè)置為24小時(shí)
  header: Authorization # 請(qǐng)求頭中攜帶令牌的字段名,默認(rèn)Authorization

1.3 數(shù)據(jù)庫表設(shè)計(jì)(用戶表)

核心是用戶表,存儲(chǔ)用戶名、密碼(加密存儲(chǔ))、角色(用于后續(xù)權(quán)限區(qū)分,簡化版用字符串表示),無需復(fù)雜設(shè)計(jì),適配登錄與權(quán)限校驗(yàn)核心需求:

CREATE TABLE `sys_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用戶主鍵',
  `username` varchar(50) NOT NULL COMMENT '用戶名(唯一)',
  `password` varchar(100) NOT NULL COMMENT '密碼(加密存儲(chǔ))',
  `role` varchar(20) NOT NULL COMMENT '用戶角色(如:admin、user)',
  `status` tinyint(1) DEFAULT 1 COMMENT '狀態(tài)(1:正常,0:禁用)',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系統(tǒng)用戶表';

說明:密碼需加密存儲(chǔ)(本文使用BCrypt加密,Spring Security自帶,無需額外引入工具),禁止明文存儲(chǔ),避免安全風(fēng)險(xiǎn)。

二、核心實(shí)戰(zhàn)一:用戶登錄接口設(shè)計(jì)

登錄接口是權(quán)限控制的入口,核心邏輯:接收前端傳入的用戶名/密碼 → 校驗(yàn)用戶名是否存在、密碼是否匹配 → 校驗(yàn)通過生成JWT令牌 → 返回令牌給前端;校驗(yàn)失敗返回錯(cuò)誤信息。

2.1 實(shí)體類與DTO設(shè)計(jì)

2.1.1 用戶實(shí)體類(SysUser)

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;

/**
 * 系統(tǒng)用戶實(shí)體類
 */
@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;

    // 狀態(tài)(1:正常,0:禁用)
    private Integer status;

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
}

2.1.2 登錄請(qǐng)求DTO(LoginDTO)

接收前端傳入的登錄參數(shù),用于參數(shù)校驗(yàn)(用戶名、密碼非空):

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

/**
 * 登錄請(qǐng)求DTO(接收前端參數(shù))
 */
@Data
public class LoginDTO {
    // 用戶名,非空校驗(yàn)
    @NotBlank(message = "用戶名不能為空")
    private String username;

    // 密碼,非空校驗(yàn)
    @NotBlank(message = "密碼不能為空")
    private String password;
}

2.1.3 登錄響應(yīng)DTO(LoginVO)

返回給前端的結(jié)果,包含JWT令牌、用戶名、角色等信息,避免返回敏感數(shù)據(jù)(如加密后的密碼):

import lombok.Data;

/**
 * 登錄響應(yīng)VO(返回給前端)
 */
@Data
public class LoginVO {
    // JWT令牌
    private String token;

    // 用戶名
    private String username;

    // 用戶角色
    private String role;

    // 令牌過期時(shí)間(單位:毫秒)
    private Long expiration;
}

2.2 Mapper、Service層實(shí)現(xiàn)(用戶校驗(yàn))

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> {
    // 根據(jù)用戶名查詢用戶(登錄核心查詢)
    SysUser selectByUsername(@Param("username") String username);
}

2.2.2 SysUserService

核心是用戶校驗(yàn)邏輯:查詢用戶、校驗(yàn)密碼、校驗(yàn)用戶狀態(tài),使用Spring Security的BCryptPasswordEncoder進(jìn)行密碼加密與匹配:

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();

    /**
     * 登錄校驗(yàn):根據(jù)用戶名和密碼校驗(yàn)用戶
     * @param username 用戶名
     * @param password 明文密碼
     * @return 校驗(yàn)通過的用戶信息
     */
    public SysUser loginCheck(String username, String password) {
        // 1. 根據(jù)用戶名查詢用戶
        SysUser user = sysUserMapper.selectByUsername(username);
        Assert.notNull(user, "用戶名不存在");

        // 2. 校驗(yàn)用戶狀態(tài)(是否禁用)
        Assert.isTrue(user.getStatus() == 1, "用戶已禁用,請(qǐng)聯(lián)系管理員");

        // 3. 校驗(yàn)密碼(明文密碼與數(shù)據(jù)庫加密密碼匹配)
        Assert.isTrue(passwordEncoder.matches(password, user.getPassword()), "密碼錯(cuò)誤");

        // 4. 校驗(yàn)通過,返回用戶信息(注意:不要返回密碼)
        user.setPassword(null);
        return user;
    }

    /**
     * 新增用戶(用于測試,密碼自動(dòng)加密)
     * @param user 用戶信息
     * @return 新增結(jié)果
     */
    public boolean addUser(SysUser user) {
        // 密碼加密后存儲(chǔ)
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        return save(user);
    }
}

2.3 Controller層:登錄接口實(shí)現(xiàn)

接收前端請(qǐng)求,調(diào)用Service層校驗(yàn)用戶,校驗(yàn)通過后調(diào)用JWT工具類生成令牌,返回登錄結(jié)果:

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過期時(shí)間
    @Value("${jwt.expiration}")
    private Long expiration;

    /**
     * 用戶登錄接口
     * @param loginDTO 登錄參數(shù)(用戶名、密碼)
     * @return 登錄結(jié)果(JWT令牌等)
     */
    @PostMapping("/login")
    public LoginVO login(@Valid @RequestBody LoginDTO loginDTO) {
        // 1. 登錄校驗(yàn)(用戶名、密碼、用戶狀態(tài))
        SysUser user = sysUserService.loginCheck(loginDTO.getUsername(), loginDTO.getPassword());

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

        // 3. 組裝返回結(jié)果
        LoginVO loginVO = new LoginVO();
        loginVO.setToken(token);
        loginVO.setUsername(user.getUsername());
        loginVO.setRole(user.getRole());
        loginVO.setExpiration(expiration);

        return loginVO;
    }
}

2.4 測試登錄接口

  1. 先調(diào)用addUser方法新增測試用戶(如username=admin,password=123456,role=admin),密碼會(huì)自動(dòng)加密存儲(chǔ);

    2. 用Postman調(diào)用POST接口:http://localhost:8080/auth/login,請(qǐng)求體如下:
    
{
  "username": "admin",
  "password": "123456"
}
  1. 響應(yīng)結(jié)果(成功):返回token、username、role、expiration,前端需存儲(chǔ)token,后續(xù)請(qǐng)求接口時(shí)攜帶該token。

三、核心實(shí)戰(zhàn)二:JWT 生成與驗(yàn)證(工具類實(shí)現(xiàn))

JWT(JSON Web Token)是一種無狀態(tài)的令牌機(jī)制,核心優(yōu)勢是無需在服務(wù)器存儲(chǔ)會(huì)話信息,僅通過令牌本身即可完成身份驗(yàn)證,適合分布式系統(tǒng)。本文封裝JWT工具類,實(shí)現(xiàn)令牌生成、驗(yàn)證、解析核心功能。

3.1 JWT工具類(JwtUtil)

核心邏輯:讀取配置文件中的密鑰和過期時(shí)間,使用jjwt依賴提供的API生成令牌、驗(yàn)證令牌有效性、解析令牌中的用戶信息(用戶ID、用戶名、角色)。

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

/**
 * JWT工具類:生成令牌、驗(yàn)證令牌、解析令牌
 */
@Component
public class JwtUtil {

    // 從配置文件讀取JWT密鑰
    @Value("${jwt.secret}")
    private String secret;

    // 從配置文件讀取令牌過期時(shí)間(毫秒)
    @Value("${jwt.expiration}")
    private Long expiration;

    // 令牌前綴(規(guī)范寫法,前端請(qǐng)求頭攜帶時(shí)需加上)
    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) {
        // 令牌過期時(shí)間 = 當(dāng)前時(shí)間 + 配置的過期時(shí)間
        Date expirationDate = new Date(System.currentTimeMillis() + expiration);

        // 構(gòu)建JWT令牌
        return Jwts.builder()
                // 載荷:存儲(chǔ)用戶核心信息(自定義字段)
                .claim("userId", userId)
                .claim("username", username)
                .claim("role", role)
                // 主題(可自定義,一般存儲(chǔ)用戶名)
                .setSubject(username)
                // 簽發(fā)時(shí)間
                .setIssuedAt(new Date())
                // 過期時(shí)間
                .setExpiration(expirationDate)
                // 簽名算法:HS256,使用配置的密鑰
                .signWith(SignatureAlgorithm.HS256, secret)
                // 壓縮令牌
                .compact();
    }

    /**
     * 驗(yàn)證JWT令牌有效性
     * @param token 待驗(yàn)證的令牌(可帶前綴,也可不帶)
     * @return true:有效,false:無效
     */
    public boolean validateToken(String token) {
        try {
            // 去除令牌前綴(如果有)
            token = token.replace(TOKEN_PREFIX, "");
            // 解析令牌,驗(yàn)證簽名和過期時(shí)間
            Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            // 捕獲所有異常(簽名錯(cuò)誤、令牌過期、格式錯(cuò)誤等)
            return false;
        }
    }

    /**
     * 解析JWT令牌,獲取載荷中的用戶信息(通用方法)
     * @param token 令牌(可帶前綴)
     * @return 載荷對(duì)象(包含自定義的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核心注意事項(xiàng)

1. 密鑰(secret):生產(chǎn)環(huán)境必須更換為復(fù)雜的隨機(jī)字符串(如32位隨機(jī)字符),避免泄露,否則會(huì)導(dǎo)致令牌被偽造;

2. 過期時(shí)間(expiration):根據(jù)業(yè)務(wù)需求設(shè)置,一般為1-24小時(shí),過期后前端需重新登錄獲取新令牌;

3. 載荷信息:僅存儲(chǔ)非敏感核心信息(如用戶ID、用戶名、角色),禁止存儲(chǔ)密碼、手機(jī)號(hào)等敏感數(shù)據(jù);

4. 令牌前綴:前端請(qǐng)求時(shí),需在請(qǐng)求頭Authorization中攜帶令牌,格式為:Bearer + 空格 + 令牌(如:Bearer eyJhbGciOiJIUzI1NiJ9...)。

四、核心實(shí)戰(zhàn)三:攔截器 / 過濾器實(shí)現(xiàn)接口權(quán)限校驗(yàn)

生成JWT令牌后,需要攔截所有需要權(quán)限的接口,校驗(yàn)令牌的有效性、用戶的權(quán)限,實(shí)現(xiàn)接口保護(hù)。本文分別實(shí)現(xiàn)兩種方式:攔截器(HandlerInterceptor)和過濾器(Filter),按需選擇,核心邏輯一致。

方式一:攔截器(HandlerInterceptor)實(shí)現(xiàn)權(quán)限校驗(yàn)

攔截器是Spring MVC提供的組件,可攔截請(qǐng)求的預(yù)處理、后處理,適合與Spring生態(tài)整合,便于獲取Spring容器中的Bean(如JwtUtil),推薦使用。

4.1.1 自定義攔截器(JwtInterceptor)

核心邏輯:攔截請(qǐng)求 → 從請(qǐng)求頭獲取令牌 → 驗(yàn)證令牌有效性 → 解析用戶信息 → 校驗(yàn)用戶權(quán)限(可選) → 放行;若令牌無效/無令牌,直接返回401未授權(quán)。

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攔截器:實(shí)現(xiàn)接口權(quán)限校驗(yàn)
 */
@Component
public class JwtInterceptor implements HandlerInterceptor {

    @Autowired
    private JwtUtil jwtUtil;

    // 從配置文件讀取請(qǐng)求頭中令牌的字段名
    @Value("${jwt.header}")
    private String jwtHeader;

    /**
     * 請(qǐng)求預(yù)處理(核心方法):在接口執(zhí)行前攔截,校驗(yàn)令牌
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1. 從請(qǐng)求頭中獲取令牌
        String token = request.getHeader(jwtHeader);

        // 2. 校驗(yàn)令牌是否存在
        if (token == null || token.trim().isEmpty()) {
            responseError(response, "未攜帶令牌,請(qǐng)先登錄");
            return false;
        }

        // 3. 驗(yàn)證令牌有效性
        if (!jwtUtil.validateToken(token)) {
            responseError(response, "令牌無效或已過期,請(qǐng)重新登錄");
            return false;
        }

        // 4. (可選)權(quán)限校驗(yàn):根據(jù)業(yè)務(wù)需求,校驗(yàn)用戶角色是否有權(quán)限訪問當(dāng)前接口
        // 示例:假設(shè)/admin/**接口只有admin角色可訪問
        String requestUri = request.getRequestURI();
        String role = jwtUtil.getRoleFromToken(token);
        if (requestUri.startsWith("/admin/") && !"admin".equals(role)) {
            responseError(response, "權(quán)限不足,無法訪問");
            return false;
        }

        // 5. 令牌校驗(yàn)通過,放行請(qǐng)求
        return true;
    }

    /**
     * 響應(yīng)錯(cuò)誤信息(統(tǒng)一返回JSON格式)
     */
    private void responseError(HttpServletResponse response, String message) throws Exception {
        response.setContentType("application/json;charset=utf-8");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 401未授權(quán)
        PrintWriter out = response.getWriter();
        out.write("{\"code\":401,\"message\":\"" + message + "\"}");
        out.flush();
        out.close();
    }
}

4.1.2 注冊(cè)攔截器(配置攔截規(guī)則)

通過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 配置:注冊(cè)JWT攔截器
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private JwtInterceptor jwtInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor)
                // 攔截所有接口
                .addPathPatterns("/**")
                // 放行接口(無需權(quán)限校驗(yàn))
                .excludePathPatterns(
                        "/auth/login", // 登錄接口
                        "/auth/register", // 注冊(cè)接口(如果有)
                        "/swagger-ui/**", // Swagger文檔(開發(fā)環(huán)境放行)
                        "/v3/api-docs/**" // Swagger接口(開發(fā)環(huán)境放行)
                );
    }
}

方式二:過濾器(Filter)實(shí)現(xiàn)權(quán)限校驗(yàn)

過濾器是Servlet提供的組件,作用于請(qǐng)求進(jìn)入Servlet之前,更底層,適合通用的權(quán)限校驗(yàn)場景。實(shí)現(xiàn)邏輯與攔截器一致,僅代碼寫法不同。

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過濾器:實(shí)現(xiàn)接口權(quán)限校驗(yàn)
 */
@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. 校驗(yàn)令牌是否存在
        if (token == null || token.trim().isEmpty()) {
            responseError(response, "未攜帶令牌,請(qǐng)先登錄");
            return;
        }

        // 3. 驗(yàn)證令牌有效性
        if (!jwtUtil.validateToken(token)) {
            responseError(response, "令牌無效或已過期,請(qǐng)重新登錄");
            return;
        }

        // 4. (可選)權(quán)限校驗(yàn)(與攔截器邏輯一致)
        String requestUri = request.getRequestURI();
        String role = jwtUtil.getRoleFromToken(token);
        if (requestUri.startsWith("/admin/") && !"admin".equals(role)) {
            responseError(response, "權(quán)限不足,無法訪問");
            return;
        }

        // 5. 放行請(qǐng)求
        filterChain.doFilter(request, response);
    }

    // 響應(yīng)錯(cuò)誤信息(與攔截器一致)
    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 注冊(cè)過濾器

通過FilterRegistrationBean注冊(cè)過濾器,指定攔截規(guī)則,與攔截器的放行/攔截邏輯一致:

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;

/**
 * 過濾器配置:注冊(cè)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); // 過濾器執(zhí)行順序,越小越先執(zhí)行
        return registrationBean;
    }
}

攔截器與過濾器對(duì)比(選型建議)

對(duì)比維度攔截器(HandlerInterceptor)過濾器(Filter)
所屬框架Spring MVCServlet
執(zhí)行時(shí)機(jī)請(qǐng)求進(jìn)入Controller之前請(qǐng)求進(jìn)入Servlet之前(更底層)
依賴Spring是,可直接注入Spring Bean(如JwtUtil)否,需通過配置注入Bean,稍繁瑣
適用場景Spring Boot項(xiàng)目,需要結(jié)合Spring生態(tài)(如權(quán)限注解)通用Java Web項(xiàng)目,無需依賴Spring
選型建議:Spring Boot項(xiàng)目優(yōu)先使用攔截器,整合更順暢,代碼更簡潔;非Spring項(xiàng)目使用過濾器。

五、實(shí)戰(zhàn)測試:接口權(quán)限校驗(yàn)效果

5.1 測試場景1:未攜帶令牌訪問需要權(quán)限的接口

調(diào)用接口:http://localhost:8080/admin/user/list(假設(shè)是管理員接口),未在請(qǐng)求頭攜帶Authorization令牌,響應(yīng)結(jié)果:

{
  "code": 401,
  "message": "未攜帶令牌,請(qǐng)先登錄"
}

5.2 測試場景2:攜帶無效令牌訪問接口

請(qǐng)求頭攜帶錯(cuò)誤令牌(如隨意輸入字符串),響應(yīng)結(jié)果:

{
  "code": 401,
  "message": "令牌無效或已過期,請(qǐng)重新登錄"
}

5.3 測試場景3:普通用戶訪問管理員接口

使用role=user的用戶登錄,獲取令牌,攜帶令牌訪問/admin/user/list接口,響應(yīng)結(jié)果:

{
  "code": 401,
  "message": "權(quán)限不足,無法訪問"
}

5.4 測試場景4:攜帶有效令牌訪問接口

使用role=admin的用戶登錄,獲取令牌,在請(qǐng)求頭添加:Authorization: Bearer 令牌,訪問/admin/user/list接口,正常返回接口數(shù)據(jù),校驗(yàn)通過。

六、常見問題與解決方案

  1. 令牌驗(yàn)證失敗,提示“簽名錯(cuò)誤”?

    解決方案:① 檢查JwtUtil中使用的密鑰(secret)是否與配置文件一致;② 確認(rèn)前端攜帶的令牌是否完整,無多余空格或字符;③ 檢查令牌生成時(shí)的簽名算法(本文用HS256)與驗(yàn)證時(shí)是否一致。
  2. 攔截器/過濾器未生效?

    解決方案:① 檢查攔截器/過濾器是否添加@Component注解(注入Spring容器);② 檢查注冊(cè)配置類(WebMvcConfig/FilterConfig)是否添加@Configuration注解;③ 確認(rèn)攔截規(guī)則是否正確,避免放行接口寫錯(cuò)。
    
  3. 前端攜帶令牌后,后端仍提示“未攜帶令牌”?

    解決方案:① 檢查前端請(qǐng)求頭字段名是否與配置文件中jwt.header一致(默認(rèn)Authorization);② 確認(rèn)令牌格式是否正確(Bearer + 空格 + 令牌);③ 排查跨域問題(若前后端分離,需配置跨域允許攜帶請(qǐng)求頭)。
    
  4. 密碼加密后,登錄時(shí)校驗(yàn)失敗?

    解決方案:① 確認(rèn)新增用戶時(shí),是否使用BCryptPasswordEncoder加密密碼;② 登錄校驗(yàn)時(shí),是否使用matches方法(明文密碼與加密密碼匹配),而非直接equals比較。
    

七、總結(jié)

本文完整實(shí)現(xiàn)了Spring Boot接口權(quán)限控制的核心流程:從用戶登錄接口設(shè)計(jì)、JWT令牌生成與驗(yàn)證,到攔截器/過濾器實(shí)現(xiàn)權(quán)限校驗(yàn),完美解決“接口無安全控制”的問題,提升項(xiàng)目專業(yè)度。核心要點(diǎn):

  • 登錄接口:核心是用戶校驗(yàn)(用戶名、密碼、狀態(tài)),密碼必須加密存儲(chǔ),避免安全風(fēng)險(xiǎn)。

  • JWT工具類:封裝生成、驗(yàn)證、解析令牌的核心方法,密鑰和過期時(shí)間需配置化,便于維護(hù)。

  • 權(quán)限校驗(yàn):優(yōu)先使用攔截器(Spring Boot適配更好),按需配置攔截/放行規(guī)則,支持角色權(quán)限區(qū)分。

掌握本文內(nèi)容后,可直接將代碼整合到畢設(shè)或?qū)嶋H項(xiàng)目中,實(shí)現(xiàn)接口的安全保護(hù)。后續(xù)可進(jìn)一步擴(kuò)展:如令牌刷新機(jī)制、更細(xì)粒度的權(quán)限控制(基于接口的權(quán)限分配)、異常統(tǒng)一處理等,讓權(quán)限體系更完善。

到此這篇關(guān)于Spring Boot項(xiàng)目開發(fā)接口權(quán)限控制之登錄+JWT實(shí)戰(zhàn)指南的文章就介紹到這了,更多相關(guān)SpringBoot接口權(quán)限控制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Servlet第一個(gè)項(xiàng)目的發(fā)布(入門)

    Servlet第一個(gè)項(xiàng)目的發(fā)布(入門)

    這篇文章主要介紹了Servlet第一個(gè)項(xiàng)目的發(fā)布,下面是用servlet實(shí)現(xiàn)的一個(gè)簡單的web項(xiàng)目,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2021-04-04
  • 在Spring應(yīng)用中進(jìn)行單元測試的解析和代碼演示

    在Spring應(yīng)用中進(jìn)行單元測試的解析和代碼演示

    在Spring應(yīng)用中進(jìn)行單元測試通常涉及到Spring TestContext Framework,它提供了豐富的注解和工具來支持單元測試和集成測試,以下是如何在Spring應(yīng)用中進(jìn)行單元測試的詳細(xì)解析和代碼演示,需要的朋友可以參考下
    2024-06-06
  • spring boot與spring mvc的區(qū)別及功能介紹

    spring boot與spring mvc的區(qū)別及功能介紹

    這篇文章主要介紹了spring boot與spring mvc的區(qū)別是什么以及spring boot和spring mvc功能介紹,感興趣的朋友一起看看吧
    2018-02-02
  • Java JVM(Java虛擬機(jī))啟動(dòng)參數(shù)的設(shè)置方式

    Java JVM(Java虛擬機(jī))啟動(dòng)參數(shù)的設(shè)置方式

    這段描述主要介紹了JavaJVM啟動(dòng)參數(shù)設(shè)置,涵蓋標(biāo)準(zhǔn)參數(shù)、非標(biāo)準(zhǔn)參數(shù)(-X)和非Stable參數(shù)(-XX)等并詳細(xì)解釋了常用參數(shù)如-Xms、-XX:MaxPermSize等等-XX:NewRatio等等等,幫助優(yōu)化Java應(yīng)用程序性能
    2026-06-06
  • Ajax實(shí)現(xiàn)搜索引擎自動(dòng)補(bǔ)全功能

    Ajax實(shí)現(xiàn)搜索引擎自動(dòng)補(bǔ)全功能

    本文主要介紹了Ajax實(shí)現(xiàn)搜索引擎自動(dòng)補(bǔ)全功能的實(shí)例解析。具有很好的參考價(jià)值。下面跟著小編一起來看下吧
    2017-04-04
  • Java實(shí)現(xiàn)PDF文檔自動(dòng)化比較的教程解析

    Java實(shí)現(xiàn)PDF文檔自動(dòng)化比較的教程解析

    在日常工作中,我們經(jīng)常需要處理大量的 PDF 文檔,本文將深入探討如何利用 Java 技術(shù),結(jié)合強(qiáng)大的 Spire.PDF for Java 庫,實(shí)現(xiàn) PDF 文檔的自動(dòng)化比較,感興趣的可以了解下
    2025-11-11
  • Java必須學(xué)會(huì)的類的繼承與多態(tài)

    Java必須學(xué)會(huì)的類的繼承與多態(tài)

    這篇文章主要介紹了Java類的繼承與多態(tài)的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下
    2020-08-08
  • Java實(shí)現(xiàn)驗(yàn)證碼的產(chǎn)生和驗(yàn)證

    Java實(shí)現(xiàn)驗(yàn)證碼的產(chǎn)生和驗(yàn)證

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)驗(yàn)證碼的產(chǎn)生和驗(yàn)證,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2012-01-01
  • SpringBoot中使用JeecgBoot的Autopoi導(dǎo)出Excel的方法步驟

    SpringBoot中使用JeecgBoot的Autopoi導(dǎo)出Excel的方法步驟

    這篇文章主要介紹了SpringBoot中使用JeecgBoot的Autopoi導(dǎo)出Excel的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • SpringBoot業(yè)務(wù)策略模式多實(shí)現(xiàn)類注入過程(Map注入)

    SpringBoot業(yè)務(wù)策略模式多實(shí)現(xiàn)類注入過程(Map注入)

    這段文章主要討論了在業(yè)務(wù)處理過程中遇到的登錄場景,通過策略模式解決代碼冗余和維護(hù)困難的問題,具體介紹了策略模式的應(yīng)用場景、實(shí)現(xiàn)方式以及帶來的好處,適用于需要根據(jù)不同條件選擇不同實(shí)現(xiàn)邏輯的場景
    2026-06-06

最新評(píng)論

黔西县| 巴塘县| 来凤县| 文昌市| 涡阳县| 永川市| 微博| 五台县| 怀仁县| 牟定县| 曲水县| 白朗县| 吉木萨尔县| 故城县| 上思县| 阆中市| 康乐县| 丽水市| 昭平县| 资兴市| 防城港市| 偏关县| 临城县| 永德县| 武平县| 磐石市| 萨嘎县| 通江县| 岳阳市| 博乐市| 永泰县| 水城县| 克山县| 黄平县| 佛教| 林甸县| 长治市| 秦安县| 额济纳旗| 亳州市| 青神县|