自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

靈活!Spring Boot 自定義注解結(jié)合參數(shù)解析器實現(xiàn)權(quán)限控制

開發(fā) 前端
在實際項目中,該方案適用于對性能和靈活性要求較高的場景,開發(fā)者可以在此基礎(chǔ)上,結(jié)合自身業(yè)務(wù)需求,進一步優(yōu)化和擴展,如 動態(tài)權(quán)限配置、RBAC(基于角色的訪問控制) 等。

在現(xiàn)代 Web 應(yīng)用開發(fā)中,權(quán)限控制是至關(guān)重要的一個環(huán)節(jié),尤其是在微服務(wù)架構(gòu)和前后端分離的模式下。如何在保證安全性的同時,兼顧開發(fā)的便捷性和代碼的可讀性,是開發(fā)者需要重點關(guān)注的問題。

Spring Boot 提供了多種方式來實現(xiàn)權(quán)限管理,例如 Spring Security,但在某些場景下,我們希望有更輕量級、靈活的權(quán)限控制方案。本篇文章將介紹如何通過 Spring Boot 3.4 中的 自定義注解 結(jié)合 AOP 和 參數(shù)解析器,實現(xiàn)一套可擴展的權(quán)限控制方案。

本方案的核心思路是:

  1. 通過 AOP 攔截 
    需要權(quán)限校驗的方法,實現(xiàn)全局權(quán)限校驗邏輯。
  2. 通過攔截器(Interceptor) 
    解析請求 Token,并將用戶信息存儲到上下文中,方便后續(xù)權(quán)限校驗使用。
  3. 使用自定義參數(shù)解析器(HandlerMethodArgumentResolver) 
    在 Controller 方法參數(shù)中,通過注解直接獲取當(dāng)前登錄用戶信息,提高代碼的可讀性。
  4. 支持 SpEL(Spring 表達式語言) 
    以動態(tài)方式獲取用戶數(shù)據(jù),實現(xiàn)更靈活的權(quán)限控制。

本文將結(jié)合代碼示例,詳細介紹該方案的實現(xiàn)方式,并最終構(gòu)建一套完整的權(quán)限校驗框架。

自定義注解

獲取當(dāng)前用戶信息的注解

package com.icoderoad.auth;


import java.lang.annotation.*;


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface AuthUser {
    /** 通過 SpEL 表達式從當(dāng)前登錄用戶信息中提取數(shù)據(jù) */
    String value() default "";
}

權(quán)限控制注解

package com.icoderoad.auth;


import java.lang.annotation.*;


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreAuthorize {
    /** 需要的權(quán)限 */
    String[] value() default {};


    /** 權(quán)限校驗邏輯(全部匹配或部分匹配) */
    Logical logic() default Logical.AND;


    enum Logical {
        AND, OR;
    }
}

核心組件實現(xiàn)

生成 Token 的工具類

package com.icoderoad.utils;


import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


import java.util.*;
import java.util.function.Function;


@Component
public class JwtUtil {
    @Value("${jwt.secret}")
    private String secret;


    @Value("${jwt.expiration}")
    private Long expiration;


    private final ObjectMapper objectMapper;


    public JwtUtil(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }


    /** 生成 JWT 令牌 */
    public String generateToken(User user) {
        try {
            String payload = objectMapper.writeValueAsString(user);
            return createToken(payload);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }


    private String createToken(String payload) {
        return Jwts.builder()
            .claim("info", payload)
            .subject("auth_token")
            .issuedAt(new Date())
            .expiration(new Date(System.currentTimeMillis() + expiration * 1000))
            .signWith(Keys.hmacShaKeyFor(secret.getBytes()))
            .compact();
    }


    /** 解析 Token 獲取用戶信息 */
    public User getUser(String token) {
        try {
            String info = (String) getClaimFromToken(token, claims -> claims.get("info"));
            return objectMapper.readValue(info, User.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    private <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        Claims claims = Jwts.parserBuilder()
            .setSigningKey(Keys.hmacShaKeyFor(secret.getBytes()))
            .build()
            .parseClaimsJws(token)
            .getBody();
        return claimsResolver.apply(claims);
    }
}

認證攔截器

package com.icoderoad.interceptor;


import com.icoderoad.utils.JwtUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.regex.*;


@Component
public class AuthInterceptor implements HandlerInterceptor {
    private static final Pattern AUTH_PATTERN = Pattern.compile("^Bearer (?<token>[a-zA-Z0-9-._~+/]+=*)$", Pattern.CASE_INSENSITIVE);


    private final JwtUtil jwtUtil;
    private final ObjectMapper objectMapper;


    public AuthInterceptor(JwtUtil jwtUtil, ObjectMapper objectMapper) {
        this.jwtUtil = jwtUtil;
        this.objectMapper = objectMapper;
    }


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
        if (authorization == null || !authorization.startsWith("Bearer ")) {
            sendError(response, "缺失 Token");
            return false;
        }


        Matcher matcher = AUTH_PATTERN.matcher(authorization);
        if (!matcher.matches()) {
            sendError(response, "無效 Token");
            return false;
        }


        User user = jwtUtil.getUser(matcher.group("token"));
        if (user == null) {
            sendError(response, "登錄失效,請重新登錄");
            return false;
        }


        SecurityContext.setUser(user);
        return true;
    }


    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        SecurityContext.clear();
    }


    private void sendError(HttpServletResponse response, String message) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().print(objectMapper.writeValueAsString(Map.of("code", -1, "message", message)));
    }
}

權(quán)限切面

package com.icoderoad.aspect;


import com.icoderoad.annotation.PreAuthorize;
import com.icoderoad.context.SecurityContext;
import com.icoderoad.exception.AuthException;
import com.icoderoad.model.User;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;


import java.util.Collections;
import java.util.Set;


@Aspect
@Component
public class PermissionAspect {
    @Around("@annotation(preAuthorize)")
    public Object checkPermission(ProceedingJoinPoint joinPoint, PreAuthorize preAuthorize) throws Throwable {
        User user = SecurityContext.getUser();
        if (user == null) {
            throw new AuthException("請先登錄");
        }


        Set<String> requiredPerms = Set.of(preAuthorize.value());
        Set<String> userPerms = user.getPermissions();
        boolean hasPermission = validatePermissions(requiredPerms, userPerms, preAuthorize.logic());


        if (!hasPermission) {
            throw new AuthException("權(quán)限不足");
        }


        return joinPoint.proceed();
    }


    private boolean validatePermissions(Set<String> required, Set<String> has, PreAuthorize.Logical logic) 		{
        return logic == PreAuthorize.Logical.AND ? has.containsAll(required) : !Collections.disjoint(required, has);
    }
}

配置攔截器

package com.icoderoad.config;


import com.icoderoad.interceptor.AuthInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;


@Configuration
public class WebConfig implements WebMvcConfigurer {
    private final AuthInterceptor authInterceptor;


    public WebConfig(AuthInterceptor authInterceptor) {
        this.authInterceptor = authInterceptor;
    }


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor).addPathPatterns("/users/**");
    }
}

測試

package com.icoderoad.controller;


import com.icoderoad.model.User;
import com.icoderoad.util.JwtUtil;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;


import java.util.Map;
import java.util.Set;


@RestController
public class LoginController {
    private final JwtUtil jwtUtil;


    public LoginController(JwtUtil jwtUtil) {
        this.jwtUtil = jwtUtil;
    }


    @GetMapping("/login")
    public ResponseEntity<Object> login(@RequestParam String username) {
        User user = new User(1L, username, Set.of("USER"));
        String token = jwtUtil.generateToken(user);
        return ResponseEntity.ok(Map.of("token", token));
    }
}

結(jié)論

通過本篇文章的學(xué)習(xí),我們基于 Spring Boot 3.4 實現(xiàn)了一套高效、靈活的權(quán)限控制方案,核心組件包括:

  1. 自定義注解 @PreAuthorize
    用于定義方法級權(quán)限控制。
  2. 攔截器(Interceptor)用于解析 Token 并獲取用戶信息。
  3. AOP 切面在方法執(zhí)行前進行權(quán)限校驗。
  4. 自定義參數(shù)解析器讓 Controller 層代碼更加簡潔優(yōu)雅。

相比傳統(tǒng)的 Spring Security 方案,本方案的優(yōu)勢在于:

  • 更加輕量級不依賴復(fù)雜的認證機制,僅需少量代碼即可實現(xiàn)。
  • 高度可擴展可以靈活適配不同的認證方式,如 JWT、OAuth2 等。
  • 代碼解耦業(yè)務(wù)邏輯與權(quán)限校驗分離,提升可維護性。

在實際項目中,該方案適用于對性能和靈活性要求較高的場景,開發(fā)者可以在此基礎(chǔ)上,結(jié)合自身業(yè)務(wù)需求,進一步優(yōu)化和擴展,如 動態(tài)權(quán)限配置、RBAC(基于角色的訪問控制) 等。

如果你在 Spring Boot 3.4 版本的開發(fā)中,正在尋找一種 既安全又高效的權(quán)限控制方案,不妨嘗試本文介紹的方法,相信會給你的項目帶來新的啟發(fā)和幫助。


責(zé)任編輯:武曉燕 來源: 路條編程
相關(guān)推薦

2025-03-10 01:00:00

Spring參數(shù)解析器

2021-03-16 10:39:29

SpringBoot參數(shù)解析器

2022-07-11 10:37:41

MapPart集合

2024-10-14 17:18:27

2013-01-14 11:40:50

IBMdW

2022-05-11 10:45:21

SpringMVC框架Map

2022-01-06 06:23:49

Swagger參數(shù)解析器

2017-08-03 17:00:54

Springmvc任務(wù)執(zhí)行器

2022-11-10 07:53:54

Spring參數(shù)校驗

2023-10-24 13:48:50

自定義注解舉值驗證

2024-02-22 08:06:45

JSON策略解析器

2024-12-27 15:37:23

2020-11-25 11:20:44

Spring注解Java

2024-09-10 10:04:47

2023-10-09 07:37:01

2018-06-21 14:46:03

Spring Boot異步調(diào)用

2023-01-13 08:11:24

2021-12-30 12:30:01

Java注解編譯器

2024-02-28 09:35:52

2022-06-27 08:16:34

JSON格式序列化
點贊
收藏

51CTO技術(shù)棧公眾號