一,接口站增加api版本号后需要做安全保障?

1,如果有接口需要登录后才能访问的,

需要用spring security增加授权

2,接口站需要增加api版本号的检验,必须是系统中定义的版本号才能访问,

避免乱填值刷接口的情况

说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest

对应的源码可以访问这里获取: https://github.com/liuhongdi/

说明:作者:刘宏缔 邮箱: 371125307@qq.com

二,演示项目的相关信息

1,项目地址:

  1. https://github.com/liuhongdi/apiversionsecurity

2,功能说明:

演示了接口站增加api版本号后的安全增强

3,项目结构:如图:

三,配置文件说明

1,pom.xml

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>
  5.  
  6. <!-- spring security -->
  7. <dependency>
  8. <groupId>org.springframework.boot</groupId>
  9. <artifactId>spring-boot-starter-security</artifactId>
  10. </dependency>

2,application.properties

  1. #error
  2. server.error.include-stacktrace=always
  3. #errorlog
  4. logging.level.org.springframework.web=trace

四,java代码说明

1,SecurityConfig.java

  1. @Configuration
  2. @EnableWebSecurity
  3. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  4.  
  5. @Override
  6. protected void configure(HttpSecurity http) throws Exception {
  7. //login和logout
  8. http.formLogin()
  9. .defaultSuccessUrl("/v2/home/home")
  10. .failureUrl("/login-error.html")
  11. .permitAll()
  12. .and()
  13. .logout();
  14.  
  15. //匹配的页面,符合限制才可访问
  16. http.authorizeRequests()
  17. .antMatchers("/v*/home/**").hasAnyRole("ADMIN","DEV")
  18. .antMatchers("/v*/goods/**").hasAnyRole("ADMIN","USER");
  19. //剩下的页面,允许访问
  20. http.authorizeRequests().anyRequest().permitAll();
  21. }
  22.  
  23. @Autowired
  24. public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
  25. //添加两个账号用来做测试
  26. auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
  27. .withUser("lhdadmin")
  28. .password(new BCryptPasswordEncoder().encode("123456"))
  29. .roles("ADMIN","USER")
  30. .and()
  31. .withUser("lhduser")
  32. .password(new BCryptPasswordEncoder().encode("123456"))
  33. .roles("USER");
  34. }
  35. }

2,Constants.java

  1. public class Constants {
  2. //api version
  3. public final static List API_VERSION_LIST = Arrays.asList("1","1.0","1.5","1.8","2","2.0");
  4. }

定义了api版本号常量

3,ApiVersionCondition.java

  1. //实现RequestCondition
  2. public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
  3. //api版本号
  4. private String apiVersion;
  5. //版本号的格式,如: /v[1-n]/api/test or /v1.5/home/api
  6. private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("/v((\\d+\\.\\d+)|(\\d+))/");
  7. public ApiVersionCondition(String apiVersion) {
  8. this.apiVersion = apiVersion;
  9. }
  10.  
  11. //将不同的筛选条件进行合并
  12. @Override
  13. public ApiVersionCondition combine(ApiVersionCondition other) {
  14. // 采用最后定义优先原则,则方法上的定义覆盖类上面的定义
  15. return new ApiVersionCondition(other.getApiVersion());
  16. }
  17.  
  18. //版本比对,用于排序
  19. @Override
  20. public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
  21. //优先匹配最新版本号
  22. return compareTo(other.getApiVersion(),this.apiVersion)?1:-1;
  23. }
  24.  
  25. //获得符合匹配条件的ApiVersionCondition
  26. @Override
  27. public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
  28. Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
  29. if (m.find()) {
  30. String version = m.group(1);
  31. //如果版本号不是list中则返回
  32. if (!Constants.API_VERSION_LIST.contains(version)) {
  33. return null;
  34. }
  35. if (compareTo(version,this.apiVersion)){
  36. return this;
  37. }
  38. }
  39. return null;
  40. }
  41. //compare version
  42. private boolean compareTo(String version1,String version2){
  43. if (!version1.contains(".")) {
  44. version1 += ".0";
  45. }
  46. if (!version2.contains(".")) {
  47. version2 += ".0";
  48. }
  49. String[] split1 = version1.split("\\.");
  50. String[] split2 = version2.split("\\.");
  51. for (int i = 0; i < split1.length; i++) {
  52. if (Integer.parseInt(split1[i])<Integer.parseInt(split2[i])){
  53. return false;
  54. }
  55. }
  56. return true;
  57. }
  58.  
  59. public String getApiVersion() {
  60. return apiVersion;
  61. }
  62. }

对版本号的解析和处理

4,ApiVersionRequestMappingHandlerMapping.java

  1. //扩展RequestMappingHandlerMapping
  2. public class ApiVersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
  3.  
  4. //类上有 @ApiVersion注解时生效
  5. @Override
  6. protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
  7. ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
  8. return createRequestCondition(apiVersion);
  9. }
  10.  
  11. //方法上有 @ApiVersion注解时生效
  12. @Override
  13. protected RequestCondition<?> getCustomMethodCondition(Method method) {
  14. ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
  15. return createRequestCondition(apiVersion);
  16. }
  17.  
  18. //返回ApiVersionCondition
  19. private RequestCondition<ApiVersionCondition> createRequestCondition(ApiVersion apiVersion) {
  20. return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value());
  21. }
  22. }

定义注解的生效条件

5,WebMvcConfig.java

  1. @Configuration
  2. public class WebMvcConfig extends WebMvcConfigurationSupport {
  3. //在获取RequestMappingHandlerMapping时
  4. //返回我们自定义的ApiVersionRequestMappingHandlerMapping
  5. @Override
  6. protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
  7. return new ApiVersionRequestMappingHandlerMapping();
  8. }
  9. }

使自定义的版本号解析生效

6,ApiVersion.java

  1. @Target({ElementType.METHOD, ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface ApiVersion {
  5. //版本号的值,从1开始
  6. String value() default "1";
  7. }

自定义版本号的注解

7,HomeController.java

  1. @RestController
  2. @RequestMapping("/{version}/home")
  3. public class HomeController {
  4.  
  5. //匹配版本v1的访问
  6. @ApiVersion("1")
  7. @GetMapping
  8. @RequestMapping("/home")
  9. public String home01(@PathVariable String version) {
  10. return "home v1 : version:" + version;
  11. }
  12.  
  13. //匹配版本v2的访问
  14. @ApiVersion("2.0")
  15. @GetMapping
  16. @RequestMapping("/home")
  17. public String home02(@PathVariable String version) {
  18. String username = SessionUtil.getCurrentUserName();
  19. String url = ServletUtil.getRequest().getRequestURL().toString();
  20. return "home v2 version: " + version+":username:"+username+";url:"+url;
  21. }
  22.  
  23. //匹配版本v1.5-2.0的访问
  24. @ApiVersion("1.5")
  25. @GetMapping
  26. @RequestMapping("/home")
  27. public String home15(@PathVariable String version) {
  28. return "home v1.5 version: " + version;
  29. }
  30.  
  31. }

7,其他非关键代码请访问github

五,测试效果

1,有权限访问的演示:

访问:

  1. http://127.0.0.1:8080/v2/home/home

会跳转到登录页面:

我们用lhdadmin这个账号登录:

可以正常访问

用未定义的版本号访问时会报错,如图:

如果一个版本号在方法没有定义,则会访问到相应的下一个版本:

如图:

没有方法标注1.8,但有方法上标注了1.5,所以访问到了注解版本号1.5的这个方法

2,演示无权限的访问:

  1. http://127.0.0.1:8080/v1.8/goods/goodsone

会跳转到登录页面

用lhduser这个账号登录

可以访问goods接口

因为没有访问home接口的授权,所以访问时会报错,如图:

六,查看spring boot版本

  1. . ____ _ __ _ _
  2. /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
  3. ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
  4. \\/ ___)| |_)| | | | | || (_| | ) ) ) )
  5. ' |____| .__|_| |_|_| |_\__, | / / / /
  6. =========|_|==============|___/=/_/_/_/
  7. :: Spring Boot :: (v2.3.3.RELEASE)

spring boot:接口站增加api版本号后的安全增强(spring boot 2.3.3)的更多相关文章

  1. spring boot: 设计接口站api的版本号,支持次版本号(spring boot 2.3.2)

    一,为什么接口站的api要使用版本号? 1,当服务端接口的功能发生改进后, 客户端如果不更新版本,    则服务端返回的功能可能不能使用,    所以在服务端功能升级后,     客户端也要相应的使用 ...

  2. Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(一)

    标题 Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(一) 技术 Spring Boot 2.Spring Security 5.JWT 运行环境 ...

  3. spring boot使用swagger生成api接口文档

    前言 在之前的文章中,使用mybatis-plus生成了对应的包,在此基础上,我们针对项目的api接口,添加swagger配置和注解,生成swagger接口文档 具体可以查看本站spring boot ...

  4. spring boot 通过feign调用api接口

    目的:远程调用服务器api,直接上步骤: 1,添加maven依赖,这是必须的: <dependency> <groupId>org.springframework.cloud& ...

  5. Spring Boot+Spring Security+JWT 实现 RESTful Api 权限控制

    摘要:用spring-boot开发RESTful API非常的方便,在生产环境中,对发布的API增加授权保护是非常必要的.现在我们来看如何利用JWT技术为API增加授权保护,保证只有获得授权的用户才能 ...

  6. spring boot:spring security给用户登录增加自动登录及图形验证码功能(spring boot 2.3.1)

    一,图形验证码的用途? 1,什么是图形验证码? 验证码(CAPTCHA)是"Completely Automated Public Turing test to tell Computers ...

  7. Spring - BeanPostProcessor接口(后处理器)讲解

    概述: BeanPostProcessor接口是众多Spring提供给开发者的bean生命周期内自定义逻辑拓展接口中的一个,其他还有类似InitializingBean,DisposableBean, ...

  8. Spring Boot实战:Restful API的构建

    上一篇文章讲解了通过Spring boot与JdbcTemplate.JPA和MyBatis的集成,实现对数据库的访问.今天主要给大家分享一下如何通过Spring boot向前端返回数据. 在现在的开 ...

  9. spring boot 接口返回值去掉为null的字段

    现在项目都是前后端分离的,返回的数据都是使用json,但有些接口的返回值存在 null或者"",这种字段不仅影响理解,还浪费带宽,需要统一做一下处理,不返回空字段,或者把NULL转 ...

随机推荐

  1. 在express中使用ES7装饰器构建路由

    在Java的Spring框架中,我们经常会看到类似于@Controller这样的注解,这类代码能够极大的提高我们代码的可读性和复用性.而在Javascript的ES7提案中,有一种新的语法叫做deco ...

  2. docker导出导入镜像docker save和docker load的用法

    1.百度搜的第一步是先将容器提交为镜像,然后用你提交的镜像去做上面的备份操作,提交为镜像后会新增一个镜像,但是感觉没有必要,直接做上面的save操作也是可以用的 百度的:docker commit 容 ...

  3. 漏洞扫描工具acunetix破解安装步骤

    Acunetix 12破解版安装教程 下载地址: 链接:https://pan.baidu.com/s/1jsKkrhOcx_O7ib7FQ6pidw 提取码:pwdj 1.下载软件压缩包文件,首先点 ...

  4. 【吴恩达课程使用】pip安装pandas失败-anaconda各种玄学T-T-从新开始搭建环境

    [吴恩达课程使用]安装pandas失败-从新开始搭建环境 在第五课第二周的任务2中,虚拟环境缺少pandas,sklearn依赖,因为用pip比较顺手,就直接使用pip安装,结果各种anaconda环 ...

  5. 第2课 - 初识makefile的结构

    第2课 - 初识makefile的结构 1. makefile 的意义 (1)makefile 用于定义源文件之间的依赖关系 (在阅读开源软件源码时,可通过Makefile掌握源码中各个文件之间的关系 ...

  6. [补题]匹配%#,%#之间的字符串重复%前的num遍

    题目 匹配%#,%#之间的字符串重复%前的num遍. 样例1: 3%acm#2%acm# 输出: acmacmacmacmacm 样例2: 3%2%acm## 输出: acmacmacmacmacm ...

  7. matlab数字图像处理-冈萨雷斯-数据类和图像类之间的转换

    亮度图像 二值图像 属于注释 数据类间的转换 图像类和类型间的转化 把一个double类的任意数组转换成[0,1]的归一化double类数组----->mat2gray 图像类和类型间的转化例题 ...

  8. python实例文本进度条

    简单的文本进度条代码 解析 引入time库 打印一行作为开始 最后也打印一个结束的标签 定义变量等于10,文本进度条大概的宽度是10 使用for循环来模拟进度,for i in range()能够不断 ...

  9. python常用sys模块

    sys.argv 命令行参数List,第一个元素是程序本身路径 sys.modules.keys() 返回所有已经导入的模块列表 sys.exc_info() 获取当前正在处理的异常类,exc_typ ...

  10. 刷题[GWCTF 2019]你的名字

    解题思路 打开发现需要输入名字,猜测会有sql注入漏洞,测试一下发现单引号被过滤了,再fuzs下看看过滤了哪些 长度为1518和1519的都有过滤,测试一下,感觉不是sql注入了.那还有什么呢,考虑了 ...