亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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)文章

  • JavaWeb中Servlet的深入理解

    JavaWeb中Servlet的深入理解

    Java Servlet 是運(yùn)行在 Web 服務(wù)器或應(yīng)用服務(wù)器上的程序,它是作為來自 Web 瀏覽器或其他 HTTP 客戶端的請求和 HTTP 服務(wù)器上的數(shù)據(jù)庫或應(yīng)用程序之間的中間層
    2021-10-10
  • SpringBoot配置Redis連接池的實(shí)現(xiàn)步驟

    SpringBoot配置Redis連接池的實(shí)現(xiàn)步驟

    本文主要介紹了SpringBoot配置Redis連接池的實(shí)現(xiàn)步驟,詳細(xì)的講解了連接池的作用、配置方式、連接池參數(shù)說明,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-03-03
  • Java操作redis設(shè)置第二天凌晨過期的解決方案

    Java操作redis設(shè)置第二天凌晨過期的解決方案

    這篇文章主要介紹了Java操作redis設(shè)置第二天凌晨過期的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java中map和flatMap的區(qū)別舉例詳解

    Java中map和flatMap的區(qū)別舉例詳解

    這篇文章主要給大家介紹了關(guān)于Java中map和flatMap區(qū)別的相關(guān)資料,在Java中Stream接口有map()和flatmap()方法,兩者都有中間流操作,并返回另一個(gè)流作為方法輸出,需要的朋友可以參考下
    2023-10-10
  • Java實(shí)現(xiàn)可配置換膚的方法示例

    Java實(shí)現(xiàn)可配置換膚的方法示例

    本文主要介紹了Java實(shí)現(xiàn)可配置換膚的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • 淺談JDK、JRE、JVM三者之間的關(guān)系

    淺談JDK、JRE、JVM三者之間的關(guān)系

    本文主要介紹了淺談JDK、JRE、JVM三者之間的關(guān)系,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • java發(fā)送http的get、post請求實(shí)現(xiàn)代碼

    java發(fā)送http的get、post請求實(shí)現(xiàn)代碼

    下面小編就為大家?guī)硪黄猨ava發(fā)送http的get、post請求實(shí)現(xiàn)代碼。小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-05-05
  • Java項(xiàng)目啟動(dòng)成功、失敗信息實(shí)時(shí)反饋提醒問題(郵件或者短信)

    Java項(xiàng)目啟動(dòng)成功、失敗信息實(shí)時(shí)反饋提醒問題(郵件或者短信)

    這篇文章主要介紹了Java項(xiàng)目啟動(dòng)成功、失敗信息實(shí)時(shí)反饋提醒問題(郵件或者短信),具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • Java String 和StringBuffer的詳解及區(qū)別

    Java String 和StringBuffer的詳解及區(qū)別

    這篇文章主要介紹了Java String 和StringBuffer的詳解及區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • java結(jié)合keytool如何實(shí)現(xiàn)非對稱加密與解密詳解

    java結(jié)合keytool如何實(shí)現(xiàn)非對稱加密與解密詳解

    這篇文章主要給大家介紹了關(guān)于java結(jié)合keytool如何實(shí)現(xiàn)非對稱加密與解密的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08

最新評論