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

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

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

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

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

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

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

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

Spring Security實(shí)現(xiàn)

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

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

通過@PreAuthorize("@pms.hasPermission('1-6')")實(shí)現(xiàn)了訪問該接口,需要有1-6這個(gè)權(quán)限。我們需要實(shí)現(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)大的功能是可以通過運(yùn)行期間執(zhí)行的表達(dá)式將值裝配到我們的屬性或構(gòu)造函數(shù)之中。

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

造輪子

自定義注解

當(dāng)然,萬變不離其宗,自定義注解我們還是需要滴。這里呢,我們僅需要定義一個(gè)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點(diǎn)間訪問
  * hasRole(‘管理員')-----具有管理員角色的人才能訪問
  * hasAllRole(‘管理員','總工程師')-----同時(shí)具有管理員、總工程師角色的人才能訪問
  *
  * Spring el
  * 文檔地址:https://docs.spring.io/spring/docs/5.1.6.RELEASE/spring-framework-reference/core.html#expressions
  */
 String value();

}

定義切面

注解定義好了,我們就需要定義切面了。這里要考慮一個(gè)點(diǎn)。我們希望的是如果方法上有注解,則對方法進(jìn)行限制,若方法上無注解,單是類上有注解,那么類上的權(quán)限注解對該類下所有的接口生效。因此,我們切點(diǎ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)限校驗(yàn)

關(guān)鍵點(diǎ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);
            //獲取解析計(jì)算的結(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)限校驗(yàn)的邏輯呢?

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

所以呢,我們還得在定義一下這個(gè)對象。進(jìn)行具體的權(quán)限校驗(yàn)邏輯處理,這里定的每一個(gè)方法都可以作為表達(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;
  }
 }

 /**
  * 是否有時(shí)間授權(quán)
  *
  * @param start 開始時(shí)間
  * @param end   結(jié)束時(shí)間
  * @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;
 }

}

實(shí)際使用

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

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

原理

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

總結(jié)

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

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

相關(guān)文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    這篇文章主要介紹了java如何獲得redis所有的key-value,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    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ù),在本文中,我們使用了幾個(gè)內(nèi)置的方法,如anyMatch()、contains()、binarySearch()等,我們將在給定的數(shù)組中找到一個(gè)值,結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05
  • Java實(shí)現(xiàn)FTP服務(wù)器功能實(shí)例代碼

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

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

最新評論

崇州市| 郴州市| 三门峡市| 咸宁市| 从江县| 安塞县| 泰宁县| 清远市| 彭泽县| 内江市| 北碚区| 璧山县| 九江市| 根河市| 南通市| 元朗区| 三河市| 准格尔旗| 浮山县| 灵石县| 图木舒克市| 金坛市| 顺昌县| 灵寿县| 清水河县| 瑞金市| 正定县| 吉林省| 钟祥市| 古交市| 鲁山县| 邢台县| 体育| 新宁县| 绥德县| 二连浩特市| 股票| 米林县| 遂昌县| 准格尔旗| 彩票|