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

Java利用SpEL表達(dá)式實現(xiàn)權(quán)限校驗

 更新時間:2024年01月26日 11:04:24   作者:重慶穿山甲  
這篇文章主要為大家詳細(xì)介紹了Java如何利用SpEL表達(dá)式實現(xiàn)權(quán)限校驗功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

對于在Springboot中,利用自定義注解+切面來實現(xiàn)接口權(quán)限的控制這個大家應(yīng)該都很熟悉,也有大量的博客來介紹整個的實現(xiàn)過程,整體來說思路如下:

  • 自定義一個權(quán)限校驗的注解,包含參數(shù)value
  • 配置在對應(yīng)的接口上
  • 定義一個切面類,指定切點
  • 在切入的方法體里寫上權(quán)限判斷的邏輯

乍一看,沒毛病,學(xué)到了,學(xué)到了~,收藏起來。但是呢,等到實際用到的時候就傻眼了,為什么呢?在實際的開發(fā)中,你會發(fā)現(xiàn),對于權(quán)限校驗的需求場景是很多的,比如:

  • 只要配置了任何角色,就可以訪問
  • 有某個權(quán)限就可以訪問
  • 放行所有請求
  • 只有超級管理員角色才可以訪問
  • 只有登錄后才可以訪問
  • 在指定時間段內(nèi)可以訪問
  • 有某個角色的情況下才可以訪問
  • 同時具有指定的多個角色情況下才可以訪問

傻眼了不,按照上面的實現(xiàn)邏輯的話怎么搞?加注解?寫各種判斷?這時候,其實我們就可以通過SpEL表達(dá)式來幫我們處理這個問題。

Spring Security實現(xiàn)

Spring Security已經(jīng)幫我們實現(xiàn)了如何通過注解,直接上例子:

@PreAuthorize("@pms.hasPermission('1-6')")
@ApiOperation("查 詢")
@GetMapping
public PageResponse<RoleCO> pageRole(RolePageQry qry){
    return roleService.listPage(qry);
}

通過@PreAuthorize("@pms.hasPermission('1-6')")實現(xiàn)了訪問該接口,需要有1-6這個權(quán)限。我們需要實現(xiàn)pms

public interface Pms {


    /**
     * 判斷接口是否有任意xxx,xxx權(quán)限
     *
     * @param permission 權(quán)限
     * @return {boolean}
     */
    default boolean hasPermission(String permission) {
        if (StrUtil.isBlank(permission)) {
            return false;
        }
        return listPermissions().contains(permission);
    }
//
    default boolean hasAnyPermission(String... permissions) {
        if (CollectionUtil.isEmpty(Arrays.asList(permissions))) {
            return false;
        }
        return listPermissions().stream().filter(StringUtils::hasText)
                .anyMatch(x -> PatternMatchUtils.simpleMatch(permissions, x));
    }

    List<String> listPermissions();
}
@Data
public class Pms implements com.jjb.saas.framework.api.system.Pms {

    /**
     * 菜單下有哪些按鈕權(quán)限
     *
     * @param
     * @return {boolean}
     */
    @Override
    public List<String> listPermissions() {
        MultiResponse multiResponse = FacadeServiceFactory.getInstance().genericInvoke("com.jjb.saas.system.client.permsgroup.facade.PermsGroupFacade", "listPermssionsByAppKey", MultiResponse.class);
        if (multiResponse.isSuccess()) {
            List datas = multiResponse.getData();
            if (ObjectUtil.isNotEmpty(datas)) {
                return datas;
            }
            return Collections.emptyList();
        }
        return Collections.emptyList();
    }
}

注入到spring中

@ComponentScan
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
@AllArgsConstructor
public class SecurityConfiguration {
    @Bean("pms")
    @ConditionalOnMissingBean
    public Pms pms() {
        Pms pms = new Pms();
        return pms;
    }
}

接下來,我們就來看看如何自己造輪子

SpEL表達(dá)式

提到SpEL,那么到底SpEL是啥呢?

SpEL的全稱為Spring Expression Language,即Spring表達(dá)式語言。是Spring3.0提供的。他最強(qiáng)大的功能是可以通過運行期間執(zhí)行的表達(dá)式將值裝配到我們的屬性或構(gòu)造函數(shù)之中。

如果有小伙伴之前沒有接觸過,不太理解這句話的含義,那么不要緊,繼續(xù)往下看,通過后續(xù)的實踐你就能明白他的作用了。

造輪子

自定義注解

當(dāng)然,萬變不離其宗,自定義注解我們還是需要滴。這里呢,我們僅需要定義一個value屬性用于接收表達(dá)式即可。

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PreAuth {

 /**
  *
  *
  * permissionAll()-----只要配置了角色就可以訪問
  * hasPermission("MENU.QUERY")-----有MENU.QUERY操作權(quán)限的角色可以訪問
  * permitAll()-----放行所有請求
  * denyAll()-----只有超級管理員角色才可訪問
  * hasAuth()-----只有登錄后才可訪問
  * hasTimeAuth(1,,10)-----只有在1-10點間訪問
  * hasRole(‘管理員')-----具有管理員角色的人才能訪問
  * hasAllRole(‘管理員','總工程師')-----同時具有管理員、總工程師角色的人才能訪問
  *
  * Spring el
  * 文檔地址:https://docs.spring.io/spring/docs/5.1.6.RELEASE/spring-framework-reference/core.html#expressions
  */
 String value();

}

定義切面

注解定義好了,我們就需要定義切面了。這里要考慮一個點。我們希望的是如果方法上有注解,則對方法進(jìn)行限制,若方法上無注解,單是類上有注解,那么類上的權(quán)限注解對該類下所有的接口生效。因此,我們切點的話要用@within注解。代碼如下:

@Around(
  "@annotation(PreAuth注解路徑) || " +
   "@within(PreAuth注解路徑)"
 )
 public Object preAuth(ProceedingJoinPoint point) throws Throwable {
  if (handleAuth(point)) {
   return point.proceed();
  }
  throw new SecureException(ResultCode.REQ_REJECT);
 }

    private boolean handleAuth(ProceedingJoinPoint point) {
        //TODO 邏輯判斷,返回true or false
    }

權(quán)限校驗

關(guān)鍵點來了。這里我們要引入SpEL。

首先,引入SpEL:

private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

然后,從注解上獲取我們需要的表達(dá)式:

MethodSignature ms = point.getSignature() instanceof MethodSignature? (MethodSignature) point.getSignature():null;
  Method method = ms.getMethod();
  // 讀取權(quán)限注解,優(yōu)先方法上,沒有則讀取類
  PreAuth preAuth = ClassUtil.getAnnotation(method, PreAuth.class);
  // 判斷表達(dá)式
  String condition = preAuth.value();
  if (StringUtil.isNotBlank(condition)) {
            //TODU 表達(dá)式解析
        }

表達(dá)式解析

private boolean handleAuth(ProceedingJoinPoint point) {
  MethodSignature ms = point.getSignature() instanceof MethodSignature? (MethodSignature) point.getSignature():null;
  Method method = ms.getMethod();
  // 讀取權(quán)限注解,優(yōu)先方法上,沒有則讀取類
  PreAuth preAuth = ClassUtil.getAnnotation(method, PreAuth.class);
  // 判斷表達(dá)式
  String condition = preAuth.value();
  if (StringUtil.isNotBlank(condition)) {
   Expression expression = EXPRESSION_PARSER.parseExpression(condition);
   // 方法參數(shù)值
   Object[] args = point.getArgs();
   StandardEvaluationContext context = getEvaluationContext(method, args);
            //獲取解析計算的結(jié)果
   return expression.getValue(context, Boolean.class);
  }
  return false;
 }
 /**
  * 獲取方法上的參數(shù)
  *
  * @param method 方法
  * @param args   變量
  * @return {SimpleEvaluationContext}
  */
private StandardEvaluationContext getEvaluationContext(Method method, Object[] args) {
  // 初始化Sp el表達(dá)式上下文,并設(shè)置 AuthFun
  StandardEvaluationContext context = new StandardEvaluationContext(new AuthFun());
  // 設(shè)置表達(dá)式支持spring bean
  context.setBeanResolver(new BeanFactoryResolver(applicationContext));
  for (int i = 0; i < args.length; i++) {
   // 讀取方法參數(shù)
   MethodParameter methodParam = ClassUtil.getMethodParameter(method, i);
   // 設(shè)置方法 參數(shù)名和值 為spel變量
   context.setVariable(methodParam.getParameterName(), args[i]);
  }
  return context;
 }

自定義解析方法

看完上面的解析處理是不是很蒙蔽,只看到了獲取表達(dá)式,獲取參數(shù),設(shè)置參數(shù),然后expression.getValue就完事了。有的同學(xué)會問,你權(quán)限校驗的邏輯呢?

別急,關(guān)鍵點在這:StandardEvaluationContext context = new StandardEvaluationContext(new AuthFun());在上文代碼中找到了吧。這個AuthFun就是我們進(jìn)行權(quán)限校驗的對象。

所以呢,我們還得在定義一下這個對象。進(jìn)行具體的權(quán)限校驗邏輯處理,這里定的每一個方法都可以作為表達(dá)式在權(quán)限注解中使用。代碼如下:

public class AuthFun {


 /**
  * 判斷角色是否具有接口權(quán)限
  *
  * @return {boolean}
  */
 public boolean permissionAll() {
  //TODO
 }

 /**
  * 判斷角色是否具有接口權(quán)限
  *
  * @param permission 權(quán)限編號,對應(yīng)菜單的MENU_CODE
  * @return {boolean}
  */
 public boolean hasPermission(String permission) {
  //TODO
 }

 /**
  * 放行所有請求
  *
  * @return {boolean}
  */
 public boolean permitAll() {
  return true;
 }

 /**
  * 只有超管角色才可訪問
  *
  * @return {boolean}
  */
 public boolean denyAll() {
  return hasRole(RoleConstant.ADMIN);
 }

 /**
  * 是否已授權(quán)
  *
  * @return {boolean}
  */
 public boolean hasAuth() {
  if(Func.isEmpty(AuthUtil.getUser())){
   // TODO 返回異常提醒
  }else{
   return true;
  }
 }

 /**
  * 是否有時間授權(quán)
  *
  * @param start 開始時間
  * @param end   結(jié)束時間
  * @return {boolean}
  */
 public boolean hasTimeAuth(Integer start, Integer end) {
  Integer hour = DateUtil.hour();
  return hour >= start && hour <= end;
 }

 /**
  * 判斷是否有該角色權(quán)限
  *
  * @param role 單角色
  * @return {boolean}
  */
 public boolean hasRole(String role) {
  return hasAnyRole(role);
 }

 /**
  * 判斷是否具有所有角色權(quán)限
  *
  * @param role 角色集合
  * @return {boolean}
  */
 public boolean hasAllRole(String... role) {
  for (String r : role) {
   if (!hasRole(r)) {
    return false;
   }
  }
  return true;
 }

 /**
  * 判斷是否有該角色權(quán)限
  *
  * @param role 角色集合
  * @return {boolean}
  */
 public boolean hasAnyRole(String... role) {
        //獲取當(dāng)前登錄用戶
  BladeUser user = AuthUtil.getUser();
  if (user == null) {
   return false;
  }
  String userRole = user.getRoleName();
  if (StringUtil.isBlank(userRole)) {
   return false;
  }
  String[] roles = Func.toStrArray(userRole);
  for (String r : role) {
   if (CollectionUtil.contains(roles, r)) {
    return true;
   }
  }
  return false;
 }

}

實際使用

在使用的時候,我們只需要在類上或者接口上,加上@PreAuth的直接,value值寫的時候要注意一下,value應(yīng)該是我們在AuthFun類中定義的方法和參數(shù),如我們定義了解析方法hasAllRole(String... role),那么在注解中,我們就可以這樣寫@PreAuth("hasAllRole('角色1','角色2')"),需要注意的是,參數(shù)要用單引號包括。

@PreAuth("hasPermission('LM_QUERY,LM_QUERY_ALL')")
public T 接口名稱....

原理

根據(jù)上面的實際使用,可以看到。SpEL表達(dá)式解析將我們注解中的"hasAllRole('角色1','角色2')"這樣的字符串,給動態(tài)解析為了hasAllRole(參數(shù)1,參數(shù)1),并調(diào)用我們注冊類中同名的方法。

總結(jié)

通過SpEL的使用,讓我們的權(quán)限配置校驗更加靈活。當(dāng)出現(xiàn)新的場景時,我們僅需要在自定的表達(dá)式解析類中增加對應(yīng)場景的解析方法即可。相對于之前的實現(xiàn)方式,這不得不說是更好的一個選擇。

以上就是Java利用SpEL表達(dá)式實現(xiàn)權(quán)限校驗的詳細(xì)內(nèi)容,更多關(guān)于Java權(quán)限校驗的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java異常 Exception類及其子類(實例講解)

    Java異常 Exception類及其子類(實例講解)

    下面小編就為大家?guī)硪黄狫ava異常 Exception類及其子類(實例講解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • java中分組統(tǒng)計的三種實現(xiàn)方式

    java中分組統(tǒng)計的三種實現(xiàn)方式

    這篇文章主要介紹了java中分組統(tǒng)計的三種實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java實戰(zhàn)之仿天貓商城系統(tǒng)的實現(xiàn)

    Java實戰(zhàn)之仿天貓商城系統(tǒng)的實現(xiàn)

    這篇文章主要介紹了如何利用Java制作一個基于SSM框架的迷你天貓商城系統(tǒng),文中采用的技術(shù)有JSP、Springboot、SpringMVC、Spring等,需要的可以參考一下
    2022-03-03
  • SpringBoot封裝響應(yīng)數(shù)據(jù)實現(xiàn)過程詳解

    SpringBoot封裝響應(yīng)數(shù)據(jù)實現(xiàn)過程詳解

    這篇文章主要介紹了SpringBoot封裝響應(yīng)數(shù)據(jù)實現(xiàn)過程,SpringBoot響應(yīng)數(shù)據(jù)封裝是指在SpringBoot應(yīng)用程序中,將返回的數(shù)據(jù)進(jìn)行封裝,以便于前端頁面或其他客戶端使用,感興趣想要詳細(xì)了解可以參考下文
    2023-05-05
  • 合并有序數(shù)組的實現(xiàn)(java與C語言)

    合并有序數(shù)組的實現(xiàn)(java與C語言)

    這篇文章主要介紹了合并有序數(shù)組的實現(xiàn)(java與C語言)的相關(guān)資料,這里對有序數(shù)組的合并分享了java版本和C語言版本的示例,需要的朋友可以參考下
    2017-08-08
  • Java JDBC的底層實現(xiàn)原理深度解析

    Java JDBC的底層實現(xiàn)原理深度解析

    JDBC是Java和數(shù)據(jù)庫之間的一個橋梁,是一個「規(guī)范」而不是一個實現(xiàn),能夠執(zhí)行SQL語句,JDBC由一組用Java語言編寫的類和接口組成,各種不同類型的數(shù)據(jù)庫都有相應(yīng)的實現(xiàn),這篇文章給大家介紹Java JDBC的底層實現(xiàn)原理,感興趣的朋友跟隨小編一起看看吧
    2025-11-11
  • java如何獲得redis所有的key-value

    java如何獲得redis所有的key-value

    這篇文章主要介紹了java如何獲得redis所有的key-value,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • 遠(yuǎn)程調(diào)用@FeignClient注解屬性使用詳解

    遠(yuǎn)程調(diào)用@FeignClient注解屬性使用詳解

    這篇文章主要為大家介紹了遠(yuǎn)程調(diào)用@FeignClient注解屬性使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • Java中如何檢查數(shù)組是否包含某整數(shù)

    Java中如何檢查數(shù)組是否包含某整數(shù)

    這篇文章主要介紹了在?Java?中檢查數(shù)組是否包含某整數(shù),在本文中,我們使用了幾個內(nèi)置的方法,如anyMatch()、contains()、binarySearch()等,我們將在給定的數(shù)組中找到一個值,結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05
  • Java實現(xiàn)FTP服務(wù)器功能實例代碼

    Java實現(xiàn)FTP服務(wù)器功能實例代碼

    FTP(File Transfer Protocol 文件傳輸協(xié)議)是Internet 上用來傳送文件的協(xié)議,本文給大家分享Java實現(xiàn)FTP服務(wù)器功能實例代碼,對java實現(xiàn)ftp服務(wù)器相關(guān)知識感興趣的朋友一起學(xué)習(xí)吧
    2015-12-12

最新評論

乐东| 同仁县| 米易县| 图木舒克市| 云林县| 通辽市| 卢湾区| 嘉定区| 丹东市| 临夏县| 泽普县| 清水县| 衡山县| 常熟市| 凉山| 岑巩县| 富宁县| 丰顺县| 铜山县| 健康| 绥阳县| 蚌埠市| 拜城县| 政和县| 台南市| 文昌市| 体育| 醴陵市| 和田市| 永州市| 丰原市| 来凤县| 酉阳| 平湖市| 称多县| 讷河市| 武陟县| 蓬溪县| 班戈县| 芷江| 临汾市|