环境:SpringBoot2.7.12
在当今的Web应用程序中,权限验证是一个重要的安全措施,用于确保只有具有适当权限的用户才能访问特定的资源。随着应用程序的规模和复杂性的增加,实现权限验证变得更加困难。为了解决这个问题,我们可以使用Spring AOP(面向切面编程)和Spring Security的组合,它们可以提供一种有效的方法来实现权限验证。
在本文中,我们将探讨如何使用Spring AOP和Spring Security来实现权限验证。我们首先介绍Spring AOP和Spring Security的概念,然后解释如何将它们结合起来实现权限验证。通过这种方式,我们可以确保只有具有适当权限的用户能够访问受保护的资源,从而提高应用程序的安全性。
Spring AOP是Spring框架中的一个模块,用于支持面向切面编程。它允许开发者在应用程序中的关键点定义切面,从而对程序流程进行干预和控制。通过使用AOP,我们可以将与业务逻辑无关的代码(如日志记录、事务管理、权限认证等)抽取出来,并将其放在独立的切面中,这样可以提高代码的可重用性和可维护性。
Spring Security是一个强大的安全框架,用于保护Web应用程序。它提供了丰富的安全特性,包括认证、授权、访问控制等。通过使用Spring Security,我们可以轻松地实现用户身份验证、角色授权、URL级别的访问控制等功能,从而确保只有经过授权的用户才能访问受保护的资源。
我们可以将Spring AOP与Spring Security结合起来实现权限验证。具体步骤如下:
通过这种方式,我们可以轻松地实现权限验证,从而提高应用程序的安全性。同时,使用Spring AOP和Spring Security还可以降低代码的耦合度,提高代码的可重用性和可维护性。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId></dependency><dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>4.4.0</version></dependency>
该过滤器的作用用来解析token,将权限信息添加到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("没有权限访问") ; return ; } // 解析token List<? extends GrantedAuthority> authorities = JwtUtils.parseAuthority(token) ; Authentication authentication = new UsernamePasswordAuthenticationToken("", "", authorities) ; SecurityContextHolder.getContext().setAuthentication(authentication) ; filterChain.doFilter(request, response) ; }}
将上面的过滤器添加到Security过滤器链中
@Configurationpublic class SecurityConfig { @Autowired void setContext(ApplicationContext context) { this.context = context ; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf().disable(); // 对所有的资源全部放行,我们只做对Controller接口的限制访问 http.authorizeRequests().anyRequest().permitAll() ; // 添加过滤器 http.addFilterBefore(new PackAuthenticationFilter(this.context), UsernamePasswordAuthenticationFilter.class) ; http.formLogin().disable() ; return http.build(); }}
该注解的作用用来标注具体的Controller接口。
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface PreAuthority { String value() default "" ; }
该切面读取接口配置的权限,验证是否具有相应的权限
@Component@Aspectpublic 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("权限不足") ; } Object ret = pjp.proceed() ; return ret ; } }
权限验证工具类
@Componentpublic class AuthorityVerify { public boolean hasAuthority(String authority) { Collection<? extends GrantedAuthority> authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities() ; return authorities.contains(new SimpleGrantedAuthority(authority)) ; } }
在上面的切面类中,如果没有权限是直接抛出的异常,所以这里定义一个全局异常对异常进行统一的处理。都比较简单,理解即可。
@RestControllerAdvicepublic class GlobalExceptionAdvice { @ExceptionHandler({Exception.class}) public Object exceptionProcess(Exception e) { return e.getMessage() ; }}
@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..." ; } }
测试用户
Map<String, Object> 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)) ;
这里模拟了一个用户信息,设置了权限集合,通过这些信息生成JWT信息。如下:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4ODg4ODgiLCJhdXRob3JpdGllcyI6WyJhcGk6Y3JlYXRlIiwiYXBpOnF1ZXJ5IiwiYXBpOnVwZGF0ZSIsImFwaTpkZWxldGUiXSwiZXhwIjoxNjk5NjE3NTM3fQ.GGLYIP2g5RZZkBoLnyQ_NWOQq_NUQylr5iZH9ouDiCM
图片
/api/save接口配置的权限是api:save,实际模拟的用户是没有这个权限的,所以这里看到的是切面中抛出的异常信息。
图片
查询接口正常访问。
以上是简单的示例,实际你应该会使用Spring Security结合数据库一起来验证管理用户的。
通过本文的介绍,我们了解了如何使用Spring AOP和Spring Security的组合来实现权限验证。通过这种方式,我们可以提高应用程序的安全性,并降低代码的耦合度,提高代码的可重用性和可维护性。希望本文能够帮助读者更好地理解和应用Spring AOP和Spring Security,为他们的应用程序开发提供有益的参考。
在上面的Controller中直接通过@PreAuthority('xxx')进行权限的设置,那我们是不是可以实现类似Spring Security提供@PreAuthorize("hasRole('xxx')")注解的功能,其中hasRole('xxx')是SpEL表达式。其实这里我们可以对切面稍加修改即可实现,部分代码如下:
初始化SpEL上下文:
@PostConstructpublic 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("不具备对应角色") ; } Object ret = pjp.proceed() ; return ret ;}
@GetMapping("/save")@PreAuthority("hasRole({'ADMIN', 'MGR'})")public Object save(HttpServletResponse response) throws Exception { return "save method invoke..." ;}
该接口只要具有ADMIN或者MGR角色的都可以访问。
本文链接:http://www.28at.com/showinfo-26-34646-0.html通过Spring AOP结合SpEL表达式:构建强大且灵活的权限控制体系
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com