说一下Spring Security中@PermitAll和@PreAuthorize的使用
作者:mmseoamin日期:2024-04-27

本文主要来自于看Java开源项目,方便理解开源项目的代码是怎么实现的,加深自己的基本功。

一、@PermitAll注解

这个是Spring Security框架中带有的一个注解,一般写在接口方法上,表示这个接口所有人都可以访问

例如

	@PostMapping("/login")
    @PermitAll
    public CommonResult login(@RequestBody User user) {
        ...
    }

然后客户端在发送http请求的时候就带有@PermitAll的所有接口都设置成permitAll()方法

这里先写一个获取所有接口中带有@PermitAll的方法

private Multimap getPermitAllUrlsFromAnnotations() {
        Multimap result = HashMultimap.create();
        // 获得接口对应的 HandlerMethod 集合
        RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)
                applicationContext.getBean("requestMappingHandlerMapping");
        Map handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
        // 获得有 @PermitAll 注解的接口
        for (Map.Entry entry : handlerMethodMap.entrySet()) {
            HandlerMethod handlerMethod = entry.getValue();
            if (!handlerMethod.hasMethodAnnotation(PermitAll.class)) {
                continue;
            }
            if (entry.getKey().getPatternsCondition() == null) {
                continue;
            }
            Set urls = entry.getKey().getPatternsCondition().getPatterns();
            // 根据请求方法,添加到 result 结果
            entry.getKey().getMethodsCondition().getMethods().forEach(requestMethod -> {
                switch (requestMethod) {
                    case GET:
                        result.putAll(HttpMethod.GET, urls);
                        break;
                    case POST:
                        result.putAll(HttpMethod.POST, urls);
                        break;
                    case PUT:
                        result.putAll(HttpMethod.PUT, urls);
                        break;
                    case DELETE:
                        result.putAll(HttpMethod.DELETE, urls);
                        break;
                }
            });
        }
        return result;
    }

http请求到来的时候将这些接口都设置成permitAll()方法

@Bean
protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        // 登出
        httpSecurity
        // 获得 @PermitAll 带来的 URL 列表,免登录
        Multimap permitAllUrls = getPermitAllUrlsFromAnnotations();
        // 设置每个请求的权限
        httpSecurity
                //  设置 @PermitAll 无需认证
                .antMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll()
                .antMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll()
                .antMatchers(HttpMethod.PUT, permitAllUrls.get(HttpMethod.PUT).toArray(new String[0])).permitAll()
                .antMatchers(HttpMethod.DELETE, permitAllUrls.get(HttpMethod.DELETE).toArray(new String[0])).permitAll()
               // 兜底规则,必须认证
                .authorizeRequests()
                .anyRequest().authenticated()
        ;
        return httpSecurity.build();
    }

这些接口都不设置任何权限,当然这是个Bean方法,还需要使用@Configuration将这个方法的类注入容器,这里就不写了

二、@PreAuthorize 注解

在开源项目中但凡使用了Spring Security框架的总会在部分接口上看到这个方法

比如

 	@PostMapping("/create")
    @Operation(summary = "新增用户")
    @PreAuthorize("@ss.hasPermission('system:user:create')")
    public CommonResult createUser(@Valid @RequestBody UserCreateReqVO reqVO) {
        Long id = userService.createUser(reqVO);
        return success(id);
    }

很明显这是个创建用户的接口,我们都能看出这个注解的大致意思,就是判断登录的人是否有添加权限

下面来详细说明一下@ss.hasPermission(‘system:user:create’)是什么意思,怎么用的

首先,@PreAuthorize是 Spring Security 内置的前置权限注解,添加在接口方法上,声明需要的权限,实现访问权限的控制。

实现原理:

当 @PreAuthorize 注解里的 Spring EL 表达式返回 false 时,表示没有权限。

而 @PreAuthorize(“@ss.hasPermission(‘system:user:create’)”) 表示调用 Bean 名字为 ss 的 hasPermission(…) 方法,方法参数为"system:user:create" 字符串。

所以你只需要找到在哪儿创建的Bean,并且Bean的名字是ss,就知道这个方法到底是怎么实现的了

说一下Spring Security中@PermitAll和@PreAuthorize的使用,在这里插入图片描述,第1张

果然