本文主要来自于看Java开源项目,方便理解开源项目的代码是怎么实现的,加深自己的基本功。
一、@PermitAll注解
这个是Spring Security框架中带有的一个注解,一般写在接口方法上,表示这个接口所有人都可以访问
例如
@PostMapping("/login")
@PermitAll
public CommonResult<xxx> login(@RequestBody User user) {
...
}
然后客户端在发送http请求的时候就带有@PermitAll的所有接口都设置成permitAll()方法
这里先写一个获取所有接口中带有@PermitAll的方法
private Multimap<HttpMethod, String> getPermitAllUrlsFromAnnotations() {
Multimap<HttpMethod, String> result = HashMultimap.create();
// 获得接口对应的 HandlerMethod 集合
RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)
applicationContext.getBean("requestMappingHandlerMapping");
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
// 获得有 @PermitAll 注解的接口
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) {
HandlerMethod handlerMethod = entry.getValue();
if (!handlerMethod.hasMethodAnnotation(PermitAll.class)) {
continue;
}
if (entry.getKey().getPatternsCondition() == null) {
continue;
}
Set<String> 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<HttpMethod, String> 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<Long> 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,就知道这个方法到底是怎么实现的了
果然