在用Spring Security项目开发中,有时候需要放通某一个接口时,我们需要在配置中把接口地址配置上,这样做有时候显得麻烦,而且不够优雅。我们能不能通过一个注解的方式,在需要放通的接口上加上该注解,这样接口就能放通了。答案肯定是可以的啦,今天我们一起来看看实现过程吧。

SpringBoot版本

本文基于的Spring Boot的版本是2.6.7

实现思路

  • 新建一个AnonymousAccess注解,该注解是应用于Controller方法上的
  • 新建一个存放所有请求方式的枚举类
  • 通过判断Controller方法上是否存在该注解
  • SecurityConfig上进行策略的配置

实现过程

新建注解

@Inherited
@Documented
@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnonymousAccess { }

新建请求枚举类

该类是存放所有的请求类型的,代码如下:

@Getter
@AllArgsConstructor
public enum RequestMethodEnum {
/**
* 搜寻 @AnonymousGetMapping
*/
GET("GET"), /**
* 搜寻 @AnonymousPostMapping
*/
POST("POST"), /**
* 搜寻 @AnonymousPutMapping
*/
PUT("PUT"), /**
* 搜寻 @AnonymousPatchMapping
*/
PATCH("PATCH"), /**
* 搜寻 @AnonymousDeleteMapping
*/
DELETE("DELETE"), /**
* 否则就是所有 Request 接口都放行
*/
ALL("All"); /**
* Request 类型
*/
private final String type; public static RequestMethodEnum find(String type) {
for (RequestMethodEnum value : RequestMethodEnum.values()) {
if (value.getType().equals(type)) {
return value;
}
}
return ALL;
}
}

判断Controller方法上是否存在该注解

SecurityConfig类中定义一个私有方法getAnonymousUrl,该方法主要作用是判断controller那些方法加上了AnonymousAccess的注解

    private Map<String, Set<String>> getAnonymousUrl(Map<RequestMappingInfo, HandlerMethod> handlerMethodMap) {
Map<String, Set<String>> anonymousUrls = new HashMap<>(8);
Set<String> get = new HashSet<>();
Set<String> post = new HashSet<>();
Set<String> put = new HashSet<>();
Set<String> patch = new HashSet<>();
Set<String> delete = new HashSet<>();
Set<String> all = new HashSet<>();
for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) { HandlerMethod handlerMethod = infoEntry.getValue();
AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
if (null != anonymousAccess) {
List<RequestMethod> requestMethods = new ArrayList<>(infoEntry.getKey().getMethodsCondition().getMethods());
RequestMethodEnum request = RequestMethodEnum.find(requestMethods.size() == 0 ? RequestMethodEnum.ALL.getType() : requestMethods.get(0).name());
switch (Objects.requireNonNull(request)) {
case GET:
get.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
case POST:
post.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
case PUT:
put.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
case PATCH:
patch.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
case DELETE:
delete.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
default:
all.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
}
}
}
anonymousUrls.put(RequestMethodEnum.GET.getType(), get);
anonymousUrls.put(RequestMethodEnum.POST.getType(), post);
anonymousUrls.put(RequestMethodEnum.PUT.getType(), put);
anonymousUrls.put(RequestMethodEnum.PATCH.getType(), patch);
anonymousUrls.put(RequestMethodEnum.DELETE.getType(), delete);
anonymousUrls.put(RequestMethodEnum.ALL.getType(), all);
return anonymousUrls;
}

SecurityConfig上进行策略的配置

通过一个SpringUtil工具类获取到requestMappingHandlerMappingBean,然后通过getAnonymousUrl方法把标注AnonymousAccess接口找出来。最后,通过antMatchers细腻化到每个 Request 类型。

    @Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// 搜寻匿名标记 url: @AnonymousAccess
RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) SpringUtil.getBean("requestMappingHandlerMapping");
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
// 获取匿名标记
Map<String, Set<String>> anonymousUrls = getAnonymousUrl(handlerMethodMap);
httpSecurity
//禁用CSRF
.csrf().disable()
.authorizeRequests()
// 自定义匿名访问所有url放行:细腻化到每个 Request 类型
// GET
.antMatchers(HttpMethod.GET,anonymousUrls.get(RequestMethodEnum.GET.getType()).toArray(new String[0])).permitAll()
// POST
.antMatchers(HttpMethod.POST,anonymousUrls.get(RequestMethodEnum.POST.getType()).toArray(new String[0])).permitAll()
// PUT
.antMatchers(HttpMethod.PUT,anonymousUrls.get(RequestMethodEnum.PUT.getType()).toArray(new String[0])).permitAll()
// PATCH
.antMatchers(HttpMethod.PATCH,anonymousUrls.get(RequestMethodEnum.PATCH.getType()).toArray(new String[0])).permitAll()
// DELETE
.antMatchers(HttpMethod.DELETE,anonymousUrls.get(RequestMethodEnum.DELETE.getType()).toArray(new String[0])).permitAll()
// 所有类型的接口都放行
.antMatchers(anonymousUrls.get(RequestMethodEnum.ALL.getType()).toArray(new String[0])).permitAll()
// 所有请求都需要认证
.anyRequest().authenticated(); }

在Controller方法上应用

在Controller上把需要的放通的接口上加上注解,即可不需要认证就可以访问了,是不是很方便呢。例如,验证码不需要认证访问的,代码如下:

    @ApiOperation(value = "获取验证码", notes = "获取验证码")
@AnonymousAccess
@GetMapping("/code")
public Object getCode(){ Captcha captcha = loginProperties.getCaptcha();
String uuid = "code-key-"+IdUtil.simpleUUID();
//当验证码类型为 arithmetic时且长度 >= 2 时,captcha.text()的结果有几率为浮点型
String captchaValue = captcha.text();
if(captcha.getCharType()-1 == LoginCodeEnum.ARITHMETIC.ordinal() && captchaValue.contains(".")){
captchaValue = captchaValue.split("\\.")[0];
}
// 保存
redisUtils.set(uuid,captchaValue,loginProperties.getLoginCode().getExpiration(), TimeUnit.MINUTES);
// 验证码信息
Map<String,Object> imgResult = new HashMap<String,Object>(2){{
put("img",captcha.toBase64());
put("uuid",uuid);
}};
return imgResult; }

效果展示

更多经常的内容请关注公众号"攻城狮成长日记"

吊炸天,Spring Security还有这种用法!的更多相关文章

  1. Spring Security 前后端分离登录,非法请求直接返回 JSON

    hello 各位小伙伴,国庆节终于过完啦,松哥也回来啦,今天开始咱们继续发干货! 关于 Spring Security,松哥之前发过多篇文章和大家聊聊这个安全框架的使用: 手把手带你入门 Spring ...

  2. spring security 3中的10个典型用法小结

    spring security 3比较庞大,但功能很强,下面小结下spring security 3中值得 注意的10个典型用法 1)多个authentication-provide可以同时使用 &l ...

  3. Spring Security笔记:Hello World

    本文演示了Spring Security的最最基本用法,二个页面(或理解成二个url),一个需要登录认证后才能访问(比如:../admin/),一个可匿名访问(比如:../welcome) 注:以下内 ...

  4. Spring Security笔记:使用数据库进行用户认证(form login using database)

    在前一节,学习了如何自定义登录页,但是用户名.密码仍然是配置在xml中的,这样显然太非主流,本节将学习如何把用户名/密码/角色存储在db中,通过db来实现用户认证 一.项目结构 与前面的示例相比,因为 ...

  5. 【JavaEE】SSH+Spring Security基础上配置AOP+log4j

    Spring Oauth2大多数情况下还是用不到的,主要使用的还是Spring+SpringMVC+Hibernate,有时候加上SpringSecurity,因此,本文及以后的文章的example中 ...

  6. 使用 Spring Security 保护 Web 应用的安全

    安全一直是 Web 应用开发中非常重要的一个方面.从安全的角度来说,需要考虑用户认证和授权两个方面.为 Web 应用增加安全方面的能力并非一件简单的事情,需要考虑不同的认证和授权机制.Spring S ...

  7. 【Spring】关于Boot应用中集成Spring Security你必须了解的那些事

    Spring Security Spring Security是Spring社区的一个顶级项目,也是Spring Boot官方推荐使用的Security框架.除了常规的Authentication和A ...

  8. Spring Security(19)——对Acl的支持

    目录 1.1           准备工作 1.2           表功能介绍 1.2.1     表acl_sid 1.2.2     表acl_class 1.2.3     表acl_obj ...

  9. Spring Security(18)——Jsp标签

    目录 1.1     authorize 1.2     authentication 1.3     accesscontrollist Spring Security也有对Jsp标签的支持的标签库 ...

随机推荐

  1. 在 VC 下清空键盘缓冲区的方法

    控制台窗口是有输入缓冲区的,当你按键后程序没有来得及处理,系统会将按键缓存,等到程序获取按键的时候,系统会把缓冲区里面之前的按键返回. // 调用控制台 API,清空之前缓冲区内的所有按键. Flus ...

  2. 基于MPC算法的车辆多目标自适应巡航控制系统研究_荆亚杰

  3. 6_稳定性_李雅普诺夫_Lyapunov

    李雅普诺夫方法参考  

  4. Spring Framework远程代码执行漏洞复现(CVE-2022-22965)

    1.漏洞描述 漏洞名称 Spring Framework远程代码执行漏洞 公开时间 2022-03-29 更新时间 2022-03-31 CVE编号 CVE-2022-22965 其他编号 QVD-2 ...

  5. ubuntu root密码问题

    安装完Ubuntu后忽然意识到没有设置root密码,不知道密码自然就无法进入根用户下.到网上搜了一下,原来是这麽回事.Ubuntu的默认root密码是随机的,即每次开机都有一个新的root密码.我们可 ...

  6. 解决IDEA中控制台输出乱码

    1. 修改VM Options(2种方法) 第一种,直接修改Tomcat中的 VM Options,这种只对当前项目有效 (1)先点击 Run -> Edit Configurations- 2 ...

  7. 使用Nginx做反向代理的配置

    安装Nginx服务之后 修改Nginx配置文件 如下server字段中主要是配置listen监听8080 端口,然后静态文件袋里到8001  后端端口代理到8000 server { listen 8 ...

  8. caioj 1000到1030都是[水题]

    caioj 1000到1030都是[水题],特此声明一下,可以不做就不要浪费时间做了

  9. 从电路到UI系列之一——常见显示控制接口的认识

    引入 我之前有两方面的使用屏幕的案例.在做的模块上需要用到屏幕来显示的时候,全是SPI屏幕,并口屏幕和IIC屏幕.在研究了驱动(或者是绘图函数更加合适)之后,我发现其实他们就是直接写显存,这个显存在面 ...

  10. 记录,element ui的日期选择器只有第一次回显成功

    首先是这个 <el-date-picker v-model="value1" type="daterange" range-separator=" ...