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

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

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

本文通過自定義注解對 Controller 層的方法實(shí)現(xiàn)訪問控制,其核心原理是基于 Spring AOP 的面向切面編程機(jī)制。系統(tǒng)在運(yùn)行時由 Spring 生成目標(biāo)類的動態(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,

    /**
     * 只需具有其中一個元素
     */
    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)識才能進(jìn)入該方法
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface RequiresRoles {

    /**
     * 需要校驗(yàn)的角色標(biāo)識
     */
    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;
        }
    }

    /**
     * 對一個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è)計請求用戶的信息(以代表當(dāng)前請求的用戶身份),同時為了便于測試,后期可直接在該部分修改用戶信息。

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)訪問時未攜帶Token

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

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

此時,需要將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)文章

  • Java定時任務(wù)詳解

    Java定時任務(wù)詳解

    定時任務(wù)在項(xiàng)目中經(jīng)常會使用到,本文主要根據(jù)博主自己使用定時的經(jīng)驗(yàn)分如下幾點(diǎn)介紹定時任務(wù):1、Quartz定時任務(wù)簡介及Spring配置Quartz定時任務(wù);2、SchedulerFactory對定時任務(wù)進(jìn)行增刪改查
    2017-04-04
  • Java中ThreadLocal?導(dǎo)致內(nèi)存?OOM?的原因分析

    Java中ThreadLocal?導(dǎo)致內(nèi)存?OOM?的原因分析

    這篇文章主要介紹了Java中ThreadLocal導(dǎo)致內(nèi)存OOM的原因分析,文章基于Java的相關(guān)內(nèi)容展開ThreadLocal導(dǎo)致內(nèi)存OOM的原因分析,需要的小伙v阿布可以參考一下
    2022-05-05
  • Springboot如何同時裝配兩個相同類型數(shù)據(jù)庫

    Springboot如何同時裝配兩個相同類型數(shù)據(jù)庫

    這篇文章主要介紹了Springboot如何同時裝配兩個相同類型數(shù)據(jù)庫,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java中如何將?int[]?數(shù)組轉(zhuǎn)換為?ArrayList(list)

    Java中如何將?int[]?數(shù)組轉(zhuǎn)換為?ArrayList(list)

    這篇文章主要介紹了Java中將?int[]?數(shù)組?轉(zhuǎn)換為?List(ArrayList),本文通過示例代碼給大家講解的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-12-12
  • Java基礎(chǔ)之刪除文本文件中特定行的內(nèi)容

    Java基礎(chǔ)之刪除文本文件中特定行的內(nèi)容

    這篇文章主要介紹了Java基礎(chǔ)之刪除文本文件中特定行的內(nèi)容,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • 詳解mybatis-plus的 mapper.xml 路徑配置的坑

    詳解mybatis-plus的 mapper.xml 路徑配置的坑

    這篇文章主要介紹了詳解mybatis-plus的 mapper.xml 路徑配置的坑,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Java 8 Function函數(shù)式接口及函數(shù)式接口實(shí)例

    Java 8 Function函數(shù)式接口及函數(shù)式接口實(shí)例

    函數(shù)式接口(Functional Interface)就是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的接口。接下來通過本文給大家介紹Java 8 Function函數(shù)式接口及函數(shù)式接口實(shí)例代碼,需要的朋友可以參考下
    2018-05-05
  • Flink流處理引擎零基礎(chǔ)速通之?dāng)?shù)據(jù)的抽取篇

    Flink流處理引擎零基礎(chǔ)速通之?dāng)?shù)據(jù)的抽取篇

    今天不分享基礎(chǔ)概念知識了,來分享一個馬上工作需要的場景,要做數(shù)據(jù)的抽取,不用kettle,想用flink。實(shí)際就是flink的sql、table層級的api
    2022-05-05
  • spring mvc @PathVariable綁定URI模板變量值方式

    spring mvc @PathVariable綁定URI模板變量值方式

    這篇文章主要介紹了spring mvc @PathVariable綁定URI模板變量值方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java刪除指定文件夾下的所有內(nèi)容的方法(包括此文件夾)

    Java刪除指定文件夾下的所有內(nèi)容的方法(包括此文件夾)

    下面小編就為大家?guī)硪黄狫ava刪除指定文件夾下的所有內(nèi)容的方法(包括此文件夾) 。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12

最新評論

黑龙江省| 云安县| 射阳县| 锡林郭勒盟| 巴林左旗| 商城县| 湛江市| 广水市| 万载县| 商城县| 皮山县| 济宁市| 苍南县| 三江| 新野县| 万全县| 乐都县| 天台县| 安龙县| 车险| 沾化县| 永胜县| 尚义县| 东辽县| 商水县| 云阳县| 施秉县| 迭部县| 本溪市| 连江县| 大渡口区| 彰化市| 富源县| 柞水县| 巩留县| 深泽县| 津南区| 昔阳县| 新巴尔虎右旗| 馆陶县| 宜宾市|