欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用SpringAOP自定義權(quán)限控制注解的實(shí)現(xiàn)示例

 更新時(shí)間:2025年08月29日 09:57:04   作者:青衫客36  
本文主要介紹了使用SpringAOP自定義權(quán)限控制注解的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

本文通過自定義注解對 Controller 層的方法實(shí)現(xiàn)訪問控制,其核心原理是基于 Spring AOP 的面向切面編程機(jī)制。系統(tǒng)在運(yùn)行時(shí)由 Spring 生成目標(biāo)類的動(dòng)態(tài)代理對象,在方法執(zhí)行前織入權(quán)限校驗(yàn)邏輯,從而實(shí)現(xiàn)對用戶訪問權(quán)限的統(tǒng)一攔截與驗(yàn)證,確保接口調(diào)用符合預(yù)設(shè)的安全規(guī)則。

package per.mjn.rbacdemo.common.security.annotation;

public enum Logical
{
    /**
     * 必須具有所有的元素
     */
    AND,

    /**
     * 只需具有其中一個(gè)元素
     */
    OR
}
package per.mjn.rbacdemo.common.security.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 登錄認(rèn)證:只有登錄之后才能進(jìn)入該方法
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE})
public @interface RequiresLogin {

}
package per.mjn.rbacdemo.common.security.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 權(quán)限認(rèn)證:必須具有指定權(quán)限才能進(jìn)入該方法
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface RequiresPermissions {

    /**
     * 需要校驗(yàn)的權(quán)限碼
     */
    String[] value() default {};

    /**
     * 驗(yàn)證模式:AND | OR, 默認(rèn)AND
     */
    Logical logical() default Logical.AND;
}
package per.mjn.rbacdemo.common.security.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 角色認(rèn)證:必須具有指定角色標(biāo)識(shí)才能進(jìn)入該方法
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface RequiresRoles {

    /**
     * 需要校驗(yàn)的角色標(biāo)識(shí)
     */
    String[] value() default {};

    /**
     * 驗(yàn)證邏輯:AND | OR,默認(rèn)AND
     */
    Logical logical() default Logical.AND;
}

定義異常

package per.mjn.rbacdemo.common.exception;

public class NotLoginException extends RuntimeException {
    public NotLoginException(String message) {
        super(message);
    }
}
package per.mjn.rbacdemo.common.exception;

public class NotPermissionException extends RuntimeException {
    public NotPermissionException(String message) {
        super("無權(quán)限訪問接口:" + message);
    }
}
package per.mjn.rbacdemo.common.exception;

public class NotRoleException extends RuntimeException {
    public NotRoleException(String message) {
        super("缺少角色:" + message);
    }
}
package per.mjn.rbacdemo.common.security.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import per.mjn.rbacdemo.common.security.annotation.RequiresLogin;
import per.mjn.rbacdemo.common.security.annotation.RequiresPermissions;
import per.mjn.rbacdemo.common.security.annotation.RequiresRoles;
import per.mjn.rbacdemo.common.security.auth.AuthUtil;

import java.lang.reflect.Method;

/**
 * 基于 Spring Aop 的注解鑒權(quán)
 */
@Aspect
@Component
public class PreAuthorizeAspect {
    /**
     * 構(gòu)建
     */
    public PreAuthorizeAspect() {
    }

    /**
     * 定義AOP簽名 (切入所有使用鑒權(quán)注解的方法)
     */

    public static final String POINTCUT_SIGN = " @annotation(per.mjn.rbacdemo.common.security.annotation.RequiresLogin) || "
            + "@annotation(per.mjn.rbacdemo.common.security.annotation.RequiresPermissions) || "
            + "@annotation(per.mjn.rbacdemo.common.security.annotation.RequiresRoles)";

    /**
     * 聲明AOP簽名
     */
    @Pointcut(POINTCUT_SIGN)
    public void pointcut() {
    }

    /**
     * 環(huán)繞切入
     * 
     * @param joinPoint 切面對象
     * @return 底層方法執(zhí)行后的返回值
     * @throws Throwable 底層方法拋出的異常
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 注解鑒權(quán)
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        checkMethodAnnotation(signature.getMethod());
        try
        {
            // 執(zhí)行原有邏輯
            Object obj = joinPoint.proceed();
            return obj;
        }
        catch (Throwable e)
        {
            throw e;
        }
    }

    /**
     * 對一個(gè)Method對象進(jìn)行注解檢查
     */
    public void checkMethodAnnotation(Method method) {
        // 校驗(yàn) @RequiresLogin 注解
        RequiresLogin requiresLogin = method.getAnnotation(RequiresLogin.class);
        if (requiresLogin != null)
        {
            AuthUtil.checkLogin();
        }

        // 校驗(yàn) @RequiresRoles 注解
        RequiresRoles requiresRoles = method.getAnnotation(RequiresRoles.class);
        if (requiresRoles != null)
        {
            AuthUtil.checkRole(requiresRoles);
        }

        // 校驗(yàn) @RequiresPermissions 注解
        RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class);
        if (requiresPermissions != null)
        {
            AuthUtil.checkPermi(requiresPermissions);
        }
    }
}
package per.mjn.rbacdemo.common.security.auth;

import per.mjn.rbacdemo.common.exception.NotLoginException;
import per.mjn.rbacdemo.common.exception.NotPermissionException;
import per.mjn.rbacdemo.common.exception.NotRoleException;
import per.mjn.rbacdemo.common.security.annotation.Logical;
import per.mjn.rbacdemo.common.security.annotation.RequiresPermissions;
import per.mjn.rbacdemo.common.security.annotation.RequiresRoles;
import per.mjn.rbacdemo.common.security.context.LoginUserHolder;
import per.mjn.rbacdemo.domain.LoginUser;

import java.util.List;

public class AuthLogic {

    // 模擬登錄用戶(實(shí)際開發(fā)中從緩存或Token中獲?。?
    public LoginUser getLoginUser() {
        LoginUser user = LoginUserHolder.get();
        if (user == null) {
            throw new NotLoginException("用戶未登錄或token無效");
        }
        return user;
    }

    public LoginUser getLoginUser(String token) {
        return getLoginUser(); // 簡化處理
    }

    public void checkLogin() {
        if (getLoginUser() == null) {
            throw new NotLoginException("未登錄");
        }
    }

    public void checkRole(RequiresRoles requiresRoles) {
        String[] roles = requiresRoles.value();
        Logical logical = requiresRoles.logical();
        List<String> userRoles = getLoginUser().getRoles();

        if (logical == Logical.AND) {
            for (String role : roles) {
                if (!userRoles.contains(role)) {
                    throw new NotRoleException(role);
                }
            }
        } else {
            for (String role : roles) {
                if (userRoles.contains(role)) return;
            }
            throw new NotRoleException(String.join(",", roles));
        }
    }

    public void checkPermi(RequiresPermissions requiresPermissions) {
        String[] perms = requiresPermissions.value();
        Logical logical = requiresPermissions.logical();
        List<String> userPerms = getLoginUser().getPermissions();

        if (logical == Logical.AND) {
            for (String perm : perms) {
                if (!userPerms.contains(perm)) {
                    throw new NotPermissionException(perm);
                }
            }
        } else {
            for (String perm : perms) {
                if (userPerms.contains(perm)) return;
            }
            throw new NotPermissionException(String.join(",", perms));
        }
    }
}
package per.mjn.rbacdemo.common.security.auth;

import per.mjn.rbacdemo.common.security.annotation.*;
import per.mjn.rbacdemo.domain.LoginUser;

public class AuthUtil {
    public static AuthLogic authLogic = new AuthLogic();

    public static void checkLogin() {
        authLogic.checkLogin();
    }

    public static void checkRole(RequiresRoles requiresRoles) {
        authLogic.checkRole(requiresRoles);
    }

    public static void checkPermi(RequiresPermissions requiresPermissions) {
        authLogic.checkPermi(requiresPermissions);
    }

    public static LoginUser getLoginUser(String token) {
        return authLogic.getLoginUser(token);
    }
}
package per.mjn.rbacdemo.common.security.context;

import per.mjn.rbacdemo.domain.LoginUser;

public class LoginUserHolder {

    private static final ThreadLocal<LoginUser> userThreadLocal = new ThreadLocal<>();

    public static void set(LoginUser loginUser) {
        userThreadLocal.set(loginUser);
    }

    public static LoginUser get() {
        return userThreadLocal.get();
    }

    public static void clear() {
        userThreadLocal.remove();
    }
}

本文中的程序只是為了快速驗(yàn)證自定義注解能否起到權(quán)限控制的作用,并未連接數(shù)據(jù)庫,所以,在請求攔截器中設(shè)計(jì)請求用戶的信息(以代表當(dāng)前請求的用戶身份),同時(shí)為了便于測試,后期可直接在該部分修改用戶信息。

package per.mjn.rbacdemo.common.security.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import per.mjn.rbacdemo.common.security.context.LoginUserHolder;
import per.mjn.rbacdemo.domain.LoginUser;

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

@Component
public class TokenInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = request.getHeader("Authorization");

        // 實(shí)際應(yīng)用中,token 應(yīng)該解密+驗(yàn)簽+查緩存等
        if (token != null && token.equals("mock-token")) {
            LoginUser loginUser = new LoginUser();
            loginUser.setUsername("testUser");

            List<String> roles = new ArrayList<>();
            roles.add("admin");

            List<String> permissions = new ArrayList<>();
//            permissions.add("system:user:query");
            permissions.add("system:user:create");

            loginUser.setRoles(roles);
            loginUser.setPermissions(permissions);

            LoginUserHolder.set(loginUser);
        }

        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        LoginUserHolder.clear();
    }
}
package per.mjn.rbacdemo.common.web;

import java.util.HashMap;

public class AjaxResult extends HashMap<String, Object> {

    public static AjaxResult success(Object data) {
        AjaxResult result = new AjaxResult();
        result.put("code", 200);
        result.put("msg", "success");
        result.put("data", data);
        return result;
    }

    public static AjaxResult error(String msg) {
        AjaxResult result = new AjaxResult();
        result.put("code", 500);
        result.put("msg", msg);
        return result;
    }
}
package per.mjn.rbacdemo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
import per.mjn.rbacdemo.common.security.interceptor.TokenInterceptor;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private TokenInterceptor tokenInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tokenInterceptor)
                .addPathPatterns("/**");
    }
}
package per.mjn.rbacdemo.controller;

import org.springframework.web.bind.annotation.*;
import per.mjn.rbacdemo.common.security.annotation.*;
import per.mjn.rbacdemo.common.security.annotation.Logical;
import per.mjn.rbacdemo.common.web.AjaxResult;
import per.mjn.rbacdemo.domain.LoginUser;

import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {

    @RequiresLogin
    @GetMapping("/profile")
    public AjaxResult getProfile() {
        return AjaxResult.success("用戶信息");
    }

    @RequiresPermissions("system:user:query")
    @GetMapping("/{userId}")
    public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId) {
        return AjaxResult.success("查詢用戶: " + (userId != null ? userId : "全部"));
    }

    @RequiresRoles(value = {"admin", "manager"}, logical = Logical.OR)
    @PostMapping("/create")
    public AjaxResult createUser() {
        LoginUser user = new LoginUser();
        user.setUsername("admin");
        user.setRoles(List.of("admin", "user"));
        user.setPermissions(List.of("system:user:query", "system:user:create"));
        return AjaxResult.success("創(chuàng)建成功");
    }
}
package per.mjn.rbacdemo.domain;

import java.util.List;
import java.util.Set;
import lombok.Data;

@Data
public class LoginUser {
    private String username;
    private List<String> roles;
    private List<String> permissions;
}
package per.mjn.rbacdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RbacDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(RbacDemoApplication.class, args);
    }

}

測試自定義權(quán)限控制注解

(1)訪問時(shí)未攜帶Token

(2)訪問時(shí)攜帶Token,但沒有該接口的訪問權(quán)限

(3)訪問時(shí)攜帶Token,且擁有該接口的訪問權(quán)限

此時(shí),需要將TokenInterceptor類中preHandle()方法中的注釋去掉

permissions.add("system:user:query");

附:pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.5.0</version>
        <relativePath/>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <groupId>per.mjn</groupId>
    <artifactId>RBACDemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>RBACDemo</name>
    <description>RBACDemo</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

        <!-- redis依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- lombok 依賴 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- MyBatis 依賴 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>

        <!-- MySQL 依賴 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <!-- Java Servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>

        <!-- Jwt -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

項(xiàng)目結(jié)構(gòu)目錄

到此這篇關(guān)于使用SpringAOP自定義權(quán)限控制注解的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)SpringAOP自定義權(quán)限控制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 手把手教你實(shí)現(xiàn)Java第三方應(yīng)用登錄

    手把手教你實(shí)現(xiàn)Java第三方應(yīng)用登錄

    本文主要介紹了手把手教你實(shí)現(xiàn)Java第三方應(yīng)用登錄,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Mybatis-plus全局id生成策略詳解

    Mybatis-plus全局id生成策略詳解

    這篇文章主要介紹了Mybatis-plus全局id生成策略詳解,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • 基于Maven的pom.xml文件詳解

    基于Maven的pom.xml文件詳解

    下面小編就為大家?guī)硪黄贛aven的pom.xml文件詳解。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • Struts2源碼分析之ParametersInterceptor攔截器

    Struts2源碼分析之ParametersInterceptor攔截器

    這篇文章主要介紹了Struts2源碼分析之ParametersInterceptor攔截器,ParametersInterceptor攔截器其主要功能是把ActionContext中的請求參數(shù)設(shè)置到ValueStack中,,需要的朋友可以參考下
    2019-06-06
  • SpringBoot使用jasypt加解密密碼的實(shí)現(xiàn)方法(二)

    SpringBoot使用jasypt加解密密碼的實(shí)現(xiàn)方法(二)

    這篇文章主要介紹了SpringBoot使用jasypt加解密密碼的實(shí)現(xiàn)方法(二),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • java使用gzip實(shí)現(xiàn)文件解壓縮示例

    java使用gzip實(shí)現(xiàn)文件解壓縮示例

    這篇文章主要介紹了java使用gzip實(shí)現(xiàn)文件解壓縮示例,需要的朋友可以參考下
    2014-03-03
  • Java詳細(xì)講解Math和Random類中有哪些常用方法

    Java詳細(xì)講解Math和Random類中有哪些常用方法

    Math類位于java.lang包中,包含很多用于科學(xué)計(jì)算的類方法,這些方法可以直接通過類名調(diào)用。Random類獲取隨機(jī)數(shù),位于java.util包中,本篇帶你了解它們的常用方法
    2022-05-05
  • SpringAOP中@Pointcut的用法詳解

    SpringAOP中@Pointcut的用法詳解

    這篇文章主要介紹了SpringAOP中@Pointcut的用法詳解,Pointcut(切點(diǎn))是面向切面編程中的一個(gè)非常重要的概念,此概念由spring框架定義,Pointcut只是一種篩選規(guī)則,需要的朋友可以參考下
    2023-08-08
  • Quarkus中ConfigSourceInterceptor的加密配置實(shí)現(xiàn)

    Quarkus中ConfigSourceInterceptor的加密配置實(shí)現(xiàn)

    這篇文章主要為大家介紹Quarkus中ConfigSourceInterceptor加密配置的實(shí)現(xiàn)方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-02-02
  • SpringCloud使用logback日志框架教程詳解

    SpringCloud使用logback日志框架教程詳解

    Logback是一個(gè)功能強(qiáng)大的日志框架,它是一個(gè)基于slf4j的日志系統(tǒng),提供了可靠的日志服務(wù),比log4j更快,更靈活,更容易使用。本文將教會(huì)你快速讓你的項(xiàng)目集成logback日志框架,需要的朋友可以參考下
    2023-05-05

最新評論