spring boot:接口站增加api版本号后的安全增强(spring boot 2.3.3)
一,接口站增加api版本号后需要做安全保障?
1,如果有接口需要登录后才能访问的,
需要用spring security增加授权
2,接口站需要增加api版本号的检验,必须是系统中定义的版本号才能访问,
避免乱填值刷接口的情况
说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest
对应的源码可以访问这里获取: https://github.com/liuhongdi/
说明:作者:刘宏缔 邮箱: 371125307@qq.com
二,演示项目的相关信息
1,项目地址:
https://github.com/liuhongdi/apiversionsecurity
2,功能说明:
演示了接口站增加api版本号后的安全增强
3,项目结构:如图:
三,配置文件说明
1,pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <!-- spring security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2,application.properties
#error
server.error.include-stacktrace=always
#errorlog
logging.level.org.springframework.web=trace
四,java代码说明
1,SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override
protected void configure(HttpSecurity http) throws Exception {
//login和logout
http.formLogin()
.defaultSuccessUrl("/v2/home/home")
.failureUrl("/login-error.html")
.permitAll()
.and()
.logout(); //匹配的页面,符合限制才可访问
http.authorizeRequests()
.antMatchers("/v*/home/**").hasAnyRole("ADMIN","DEV")
.antMatchers("/v*/goods/**").hasAnyRole("ADMIN","USER");
//剩下的页面,允许访问
http.authorizeRequests().anyRequest().permitAll();
} @Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//添加两个账号用来做测试
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("lhdadmin")
.password(new BCryptPasswordEncoder().encode("123456"))
.roles("ADMIN","USER")
.and()
.withUser("lhduser")
.password(new BCryptPasswordEncoder().encode("123456"))
.roles("USER");
}
}
2,Constants.java
public class Constants {
//api version
public final static List API_VERSION_LIST = Arrays.asList("1","1.0","1.5","1.8","2","2.0");
}
定义了api版本号常量
3,ApiVersionCondition.java
//实现RequestCondition
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
//api版本号
private String apiVersion;
//版本号的格式,如: /v[1-n]/api/test or /v1.5/home/api
private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("/v((\\d+\\.\\d+)|(\\d+))/");
public ApiVersionCondition(String apiVersion) {
this.apiVersion = apiVersion;
} //将不同的筛选条件进行合并
@Override
public ApiVersionCondition combine(ApiVersionCondition other) {
// 采用最后定义优先原则,则方法上的定义覆盖类上面的定义
return new ApiVersionCondition(other.getApiVersion());
} //版本比对,用于排序
@Override
public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
//优先匹配最新版本号
return compareTo(other.getApiVersion(),this.apiVersion)?1:-1;
} //获得符合匹配条件的ApiVersionCondition
@Override
public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
if (m.find()) {
String version = m.group(1);
//如果版本号不是list中则返回
if (!Constants.API_VERSION_LIST.contains(version)) {
return null;
}
if (compareTo(version,this.apiVersion)){
return this;
}
}
return null;
}
//compare version
private boolean compareTo(String version1,String version2){
if (!version1.contains(".")) {
version1 += ".0";
}
if (!version2.contains(".")) {
version2 += ".0";
}
String[] split1 = version1.split("\\.");
String[] split2 = version2.split("\\.");
for (int i = 0; i < split1.length; i++) {
if (Integer.parseInt(split1[i])<Integer.parseInt(split2[i])){
return false;
}
}
return true;
} public String getApiVersion() {
return apiVersion;
}
}
对版本号的解析和处理
4,ApiVersionRequestMappingHandlerMapping.java
//扩展RequestMappingHandlerMapping
public class ApiVersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping { //类上有 @ApiVersion注解时生效
@Override
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
return createRequestCondition(apiVersion);
} //方法上有 @ApiVersion注解时生效
@Override
protected RequestCondition<?> getCustomMethodCondition(Method method) {
ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
return createRequestCondition(apiVersion);
} //返回ApiVersionCondition
private RequestCondition<ApiVersionCondition> createRequestCondition(ApiVersion apiVersion) {
return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value());
}
}
定义注解的生效条件
5,WebMvcConfig.java
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
//在获取RequestMappingHandlerMapping时
//返回我们自定义的ApiVersionRequestMappingHandlerMapping
@Override
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new ApiVersionRequestMappingHandlerMapping();
}
}
使自定义的版本号解析生效
6,ApiVersion.java
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiVersion {
//版本号的值,从1开始
String value() default "1";
}
自定义版本号的注解
7,HomeController.java
@RestController
@RequestMapping("/{version}/home")
public class HomeController { //匹配版本v1的访问
@ApiVersion("1")
@GetMapping
@RequestMapping("/home")
public String home01(@PathVariable String version) {
return "home v1 : version:" + version;
} //匹配版本v2的访问
@ApiVersion("2.0")
@GetMapping
@RequestMapping("/home")
public String home02(@PathVariable String version) {
String username = SessionUtil.getCurrentUserName();
String url = ServletUtil.getRequest().getRequestURL().toString();
return "home v2 version: " + version+":username:"+username+";url:"+url;
} //匹配版本v1.5-2.0的访问
@ApiVersion("1.5")
@GetMapping
@RequestMapping("/home")
public String home15(@PathVariable String version) {
return "home v1.5 version: " + version;
} }
7,其他非关键代码请访问github
五,测试效果
1,有权限访问的演示:
访问:
http://127.0.0.1:8080/v2/home/home
会跳转到登录页面:
我们用lhdadmin这个账号登录:
可以正常访问
用未定义的版本号访问时会报错,如图:
如果一个版本号在方法没有定义,则会访问到相应的下一个版本:
如图:
没有方法标注1.8,但有方法上标注了1.5,所以访问到了注解版本号1.5的这个方法
2,演示无权限的访问:
http://127.0.0.1:8080/v1.8/goods/goodsone
会跳转到登录页面
用lhduser这个账号登录
可以访问goods接口
因为没有访问home接口的授权,所以访问时会报错,如图:
六,查看spring boot版本
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.3.RELEASE)
spring boot:接口站增加api版本号后的安全增强(spring boot 2.3.3)的更多相关文章
- spring boot: 设计接口站api的版本号,支持次版本号(spring boot 2.3.2)
一,为什么接口站的api要使用版本号? 1,当服务端接口的功能发生改进后, 客户端如果不更新版本, 则服务端返回的功能可能不能使用, 所以在服务端功能升级后, 客户端也要相应的使用 ...
- Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(一)
标题 Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(一) 技术 Spring Boot 2.Spring Security 5.JWT 运行环境 ...
- spring boot使用swagger生成api接口文档
前言 在之前的文章中,使用mybatis-plus生成了对应的包,在此基础上,我们针对项目的api接口,添加swagger配置和注解,生成swagger接口文档 具体可以查看本站spring boot ...
- spring boot 通过feign调用api接口
目的:远程调用服务器api,直接上步骤: 1,添加maven依赖,这是必须的: <dependency> <groupId>org.springframework.cloud& ...
- Spring Boot+Spring Security+JWT 实现 RESTful Api 权限控制
摘要:用spring-boot开发RESTful API非常的方便,在生产环境中,对发布的API增加授权保护是非常必要的.现在我们来看如何利用JWT技术为API增加授权保护,保证只有获得授权的用户才能 ...
- spring boot:spring security给用户登录增加自动登录及图形验证码功能(spring boot 2.3.1)
一,图形验证码的用途? 1,什么是图形验证码? 验证码(CAPTCHA)是"Completely Automated Public Turing test to tell Computers ...
- Spring - BeanPostProcessor接口(后处理器)讲解
概述: BeanPostProcessor接口是众多Spring提供给开发者的bean生命周期内自定义逻辑拓展接口中的一个,其他还有类似InitializingBean,DisposableBean, ...
- Spring Boot实战:Restful API的构建
上一篇文章讲解了通过Spring boot与JdbcTemplate.JPA和MyBatis的集成,实现对数据库的访问.今天主要给大家分享一下如何通过Spring boot向前端返回数据. 在现在的开 ...
- spring boot 接口返回值去掉为null的字段
现在项目都是前后端分离的,返回的数据都是使用json,但有些接口的返回值存在 null或者"",这种字段不仅影响理解,还浪费带宽,需要统一做一下处理,不返回空字段,或者把NULL转 ...
随机推荐
- MySQL 8 安装教程(个人版)+创建用户
Mysql 8的安装教程 解压到指定目录如:D:\WinInstall\mysql-8.0.19-winx64这时候你需要在根目录下创建两个文件,分别是data文件夹和my.ini文件,然后使用编辑器 ...
- java中双亲委派机制(+总结)
类加载器 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段"加载"过程中,需要通过一个类的全限定名来获 ...
- PHP面试总结(转)
PHP面试总结 从8月15号来到北京一直到今天,一月有余.来的这段时间一直准备笔试面试,大大小小的公司,乱七八糟面了10多家,近期才安顿下来.面试的这段时间感觉自己成长了不少.初来到这个陌生的城市 ...
- [程序员代码面试指南]递归和动态规划-机器人达到指定位置方法数(一维DP待做)(DP)
题目描述 一行N个位置1到N,机器人初始位置M,机器人可以往左/右走(只能在位置范围内),规定机器人必须走K步,最终到位置P.输入这四个参数,输出机器人可以走的方法数. 解题思路 DP 方法一:时间复 ...
- Istio中的流量配置
Istio中的流量配置 目录 Istio中的流量配置 Istio注入的容器 Istio-init istio-proxy Envoy架构 Pilot-agent生成的初始配置文件 Envoy管理接口获 ...
- zookeeper(3) 持久化
zookeeper为了防止,系统宕机或重启导致的数据丢失,会对数据进行定时持久化.有两种持久化方式: 1.为每次事务操作记录到日志文件,这样就可以通过执行这些日志文件来恢复数据. 2.为了加快ZooK ...
- centos7 yum安装redis及常用命令
redis是什么 Redis是一种基于内存的数据结构存储,可持久化的日志型.Key-Value数据库.使用关系型数据库的站点达到一定并发量的时候,往往在磁盘IO上会有瓶颈,这时候配合redis就有一定 ...
- Vue 3.0 来了,我们该做些什么?
靓仔路过,不要错过 想必 Vue3.0 发布这件事,大家都知道了. 我也是从朋友圈的转发得知此事,博客平台.公众号.朋友圈基本都有这么一条新闻,可见 Vue3.0 的被期待程度,因为 React 16 ...
- Java基础一篇过(二)泛型
一.啥是泛型 概述 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,即所操作的数据类型被指定为一个参数. 格式 类名<类型名> 标记符 E - Element (在集合中使用 ...
- kafka学习(五)Spring Boot 整合 Kafka
文章更新时间:2020/06/08 一.创建Spring boot 工程 创建过程不再描述,创建后的工程结构如下: POM文件中要加入几个依赖: <?xml version="1.0& ...