一,接口站增加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)