日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
通過(guò)Spring AOP結(jié)合SpEL表達(dá)式:構(gòu)建強(qiáng)大且靈活的權(quán)限控制體系

環(huán)境:SpringBoot2.7.12

1.前言

在當(dāng)今的Web應(yīng)用程序中,權(quán)限驗(yàn)證是一個(gè)重要的安全措施,用于確保只有具有適當(dāng)權(quán)限的用戶才能訪問特定的資源。隨著應(yīng)用程序的規(guī)模和復(fù)雜性的增加,實(shí)現(xiàn)權(quán)限驗(yàn)證變得更加困難。為了解決這個(gè)問題,我們可以使用Spring AOP(面向切面編程)和Spring Security的組合,它們可以提供一種有效的方法來(lái)實(shí)現(xiàn)權(quán)限驗(yàn)證。

在本文中,我們將探討如何使用Spring AOP和Spring Security來(lái)實(shí)現(xiàn)權(quán)限驗(yàn)證。我們首先介紹Spring AOP和Spring Security的概念,然后解釋如何將它們結(jié)合起來(lái)實(shí)現(xiàn)權(quán)限驗(yàn)證。通過(guò)這種方式,我們可以確保只有具有適當(dāng)權(quán)限的用戶能夠訪問受保護(hù)的資源,從而提高應(yīng)用程序的安全性。

一、Spring AOP介紹

Spring AOP是Spring框架中的一個(gè)模塊,用于支持面向切面編程。它允許開發(fā)者在應(yīng)用程序中的關(guān)鍵點(diǎn)定義切面,從而對(duì)程序流程進(jìn)行干預(yù)和控制。通過(guò)使用AOP,我們可以將與業(yè)務(wù)邏輯無(wú)關(guān)的代碼(如日志記錄、事務(wù)管理、權(quán)限認(rèn)證等)抽取出來(lái),并將其放在獨(dú)立的切面中,這樣可以提高代碼的可重用性和可維護(hù)性。

二、Spring Security介紹

Spring Security是一個(gè)強(qiáng)大的安全框架,用于保護(hù)Web應(yīng)用程序。它提供了豐富的安全特性,包括認(rèn)證、授權(quán)、訪問控制等。通過(guò)使用Spring Security,我們可以輕松地實(shí)現(xiàn)用戶身份驗(yàn)證、角色授權(quán)、URL級(jí)別的訪問控制等功能,從而確保只有經(jīng)過(guò)授權(quán)的用戶才能訪問受保護(hù)的資源。

三、Spring AOP與Spring Security的組合

我們可以將Spring AOP與Spring Security結(jié)合起來(lái)實(shí)現(xiàn)權(quán)限驗(yàn)證。具體步驟如下:

  1. 定義一個(gè)Aspect切面,用于實(shí)現(xiàn)權(quán)限驗(yàn)證邏輯。該Aspect可以攔截用戶對(duì)受保護(hù)資源的訪問請(qǐng)求,并驗(yàn)證其權(quán)限。
  2. 定義一個(gè)Filter,該過(guò)濾器實(shí)現(xiàn)token的解析,將權(quán)限信息保存到當(dāng)前的安全上下文中,最后添加到Security的過(guò)濾器鏈中。
  3. 在Aspect中,我們可以使用Spring Security提供的API來(lái)獲取當(dāng)前用戶的身份信息、角色等信息,并根據(jù)業(yè)務(wù)需求判斷用戶是否具有訪問受保護(hù)資源的權(quán)限。
  4. 如果用戶沒有足夠的權(quán)限訪問受保護(hù)資源,我們可以拋出一個(gè)異常,以阻止用戶繼續(xù)訪問。
  5. 如果用戶具有足夠的權(quán)限訪問受保護(hù)資源,我們可以允許用戶繼續(xù)訪問該資源。

通過(guò)這種方式,我們可以輕松地實(shí)現(xiàn)權(quán)限驗(yàn)證,從而提高應(yīng)用程序的安全性。同時(shí),使用Spring AOP和Spring Security還可以降低代碼的耦合度,提高代碼的可重用性和可維護(hù)性。

2. 權(quán)限認(rèn)證實(shí)現(xiàn)

相關(guān)依賴


  org.springframework.boot
  spring-boot-starter-security


  org.springframework.boot
  spring-boot-starter-aop




    com.auth0
    java-jwt
    4.4.0

權(quán)限認(rèn)證過(guò)濾器

該過(guò)濾器的作用用來(lái)解析token,將權(quán)限信息添加到SecurityContext上下文中

public class PackAuthenticationFilter extends OncePerRequestFilter {


  public static final String TOKEN_NAME = "x-api-token" ;
  
  @SuppressWarnings("unused")
  private ApplicationContext context ;
  
  public PackAuthenticationFilter(ApplicationContext context) {
    this.context = context ;
  }
  
  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {
    String token = request.getHeader(TOKEN_NAME) ;
    if (!StringUtils.hasLength(token)) {
      response.setContentType("text/html;charset=UTF-8") ;
      response.getWriter().println("沒有權(quán)限訪問") ;
      return ;
    } 
    // 解析token
    List authorities = JwtUtils.parseAuthority(token) ;
    Authentication authentication = new UsernamePasswordAuthenticationToken("", "", authorities) ;
    SecurityContextHolder.getContext().setAuthentication(authentication) ;
    filterChain.doFilter(request, response) ;
  }


}

安全配置類

將上面的過(guò)濾器添加到Security過(guò)濾器鏈中

@Configuration
public class SecurityConfig {
  
  @Autowired
  void setContext(ApplicationContext context) {
    this.context = context ;
  }
  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.csrf().disable();
    // 對(duì)所有的資源全部放行,我們只做對(duì)Controller接口的限制訪問
    http.authorizeRequests().anyRequest().permitAll() ;
    // 添加過(guò)濾器
    http.addFilterBefore(new PackAuthenticationFilter(this.context), UsernamePasswordAuthenticationFilter.class) ;
    http.formLogin().disable() ;
    return http.build();
  }


}

自定義注解

該注解的作用用來(lái)標(biāo)注具體的Controller接口。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PreAuthority {
  
  String value() default "" ;
  
}

驗(yàn)證切面

該切面讀取接口配置的權(quán)限,驗(yàn)證是否具有相應(yīng)的權(quán)限

@Component
@Aspect
public class AuthenticationAspect {
  
  private AuthorityVerify authorityVerify ;
  
  public AuthenticationAspect(AuthorityVerify authorityVerify) {
    this.authorityVerify = authorityVerify ;
  }
  
  @Pointcut("@annotation(auth)")
  private void authority(PreAuthority auth) {}
  
  @Around("authority(auth)")
  public Object test(ProceedingJoinPoint pjp, PreAuthority auth) throws Throwable {
    String authority = auth.value() ;
    boolean permit = this.authorityVerify.hasAuthority(authority) ;
    if (!permit) {
      throw new RuntimeException("權(quán)限不足") ;
    }
    Object ret = pjp.proceed() ;
    return ret ;
  }
  
}

權(quán)限驗(yàn)證工具類

@Component
public class AuthorityVerify {


  public boolean hasAuthority(String authority) {
    Collection authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities() ;
    return authorities.contains(new SimpleGrantedAuthority(authority)) ;
  }
  
}

全局異常處理

在上面的切面類中,如果沒有權(quán)限是直接拋出的異常,所以這里定義一個(gè)全局異常對(duì)異常進(jìn)行統(tǒng)一的處理。都比較簡(jiǎn)單,理解即可。

@RestControllerAdvice
public class GlobalExceptionAdvice {
  
  @ExceptionHandler({Exception.class})
  public Object exceptionProcess(Exception e) {
    return e.getMessage() ;
  }
}

測(cè)試接口

@RestController
@RequestMapping("/api")
public class ApiController {


  @GetMapping("/save")
  @PreAuthority("api:save")
  public Object save(HttpServletResponse response) throws Exception {
    return "save method invoke..." ;
  }
  
  @GetMapping("/{id}")
  @PreAuthority("api:query")
  public Object query(@PathVariable("id") Integer id) {
    return "query method invoke..." ;
  }
  
}

測(cè)試用戶

Map map = new HashMap<>() ;
map.put("userId", "888888") ;
map.put("authorities", List.of("api:create", "api:query", "api:update", "api:delete")) ;
String token = createToken(map) ;
System.out.println(token) ;
String content = parseToken(token);
System.out.println(content) ;
System.out.println(">>>>>>>>>>>>>>>>>>>>>") ;
System.out.println(parseAuthority(token)) ;

這里模擬了一個(gè)用戶信息,設(shè)置了權(quán)限集合,通過(guò)這些信息生成JWT信息。如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4ODg4ODgiLCJhdXRob3JpdGllcyI6WyJhcGk6Y3JlYXRlIiwiYXBpOnF1ZXJ5IiwiYXBpOnVwZGF0ZSIsImFwaTpkZWxldGUiXSwiZXhwIjoxNjk5NjE3NTM3fQ.GGLYIP2g5RZZkBoLnyQ_NWOQq_NUQylr5iZH9ouDiCM

測(cè)試結(jié)果

圖片

/api/save接口配置的權(quán)限是api:save,實(shí)際模擬的用戶是沒有這個(gè)權(quán)限的,所以這里看到的是切面中拋出的異常信息。

圖片

查詢接口正常訪問。

以上是簡(jiǎn)單的示例,實(shí)際你應(yīng)該會(huì)使用Spring Security結(jié)合數(shù)據(jù)庫(kù)一起來(lái)驗(yàn)證管理用戶的。

通過(guò)本文的介紹,我們了解了如何使用Spring AOP和Spring Security的組合來(lái)實(shí)現(xiàn)權(quán)限驗(yàn)證。通過(guò)這種方式,我們可以提高應(yīng)用程序的安全性,并降低代碼的耦合度,提高代碼的可重用性和可維護(hù)性。希望本文能夠幫助讀者更好地理解和應(yīng)用Spring AOP和Spring Security,為他們的應(yīng)用程序開發(fā)提供有益的參考。

思考:

在上面的Controller中直接通過(guò)@PreAuthority('xxx')進(jìn)行權(quán)限的設(shè)置,那我們是不是可以實(shí)現(xiàn)類似Spring Security提供@PreAuthorize("hasRole('xxx')")注解的功能,其中hasRole('xxx')是SpEL表達(dá)式。其實(shí)這里我們可以對(duì)切面稍加修改即可實(shí)現(xiàn),部分代碼如下:

初始化SpEL上下文:

@PostConstruct
public void init() {
  SpelParserConfiguration config = new SpelParserConfiguration(true, true);
  parser = new SpelExpressionParser(config) ;
  context = new StandardEvaluationContext() ;
  context.setRootObject(this.authorityVerify) ;
}

修改切面

@Around("authority(auth)")
public Object test(ProceedingJoinPoint pjp, PreAuthority auth) throws Throwable {
  String authority = auth.value() ;
  boolean permit = this.parser.parseExpression(authority).getValue(this.context, Boolean.class) ;
  if (!permit) {
    throw new RuntimeException("不具備對(duì)應(yīng)角色") ;
  }
  Object ret = pjp.proceed() ;
  return ret ;
}

修改接口

@GetMapping("/save")
@PreAuthority("hasRole({'ADMIN', 'MGR'})")
public Object save(HttpServletResponse response) throws Exception {
  return "save method invoke..." ;
}

該接口只要具有ADMIN或者M(jìn)GR角色的都可以訪問。


網(wǎng)頁(yè)標(biāo)題:通過(guò)Spring AOP結(jié)合SpEL表達(dá)式:構(gòu)建強(qiáng)大且靈活的權(quán)限控制體系
地址分享:http://www.5511xx.com/article/dpdsipe.html