问题背景:
项目用的swagger生成的接口文档,同时也有security权限验证,为了方便后端自己测试,所以接口测试直接访问swagger;
但是security如果没有放行swagger的话,本地是访问不到swagger的,同时前端要访问后端接口,也有跨域问题;
Security没有放行swagger访问的时候,用swagger请求接口会报错:
我这样配置SecurityConfig之后,解决了swagger访问的问题:
@Override
public void configure(WebSecurity web) throws Exception {
// v1是接口访问前缀,注意与自己的项目区别
web.ignoring().antMatchers("/v1/**");
}
但是,前端的跨域问题又出现了, 前端报错403;
查了一下资料,
WebSecurity和HttpSecurity的区别主要是:
以下是SecurityConfig配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MenusService menuService;
/**
* 设置验证信息
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
@Override
public void configure(WebSecurity web) throws Exception {
// web.ignoring是直接绕开spring security的所有filter,直接跳过验证
web.ignoring().antMatchers("/v1/**");
}
/**
* 设置权限信息
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//主要是看UrlMatchVoter,所有的权限检查都在UrlMatchVoter
http.cors()
.and()
.csrf()
.disable()
.authorizeRequests()
// http.permitAll不会绕开springsecurity验证,相当于是允许该路径通过
.antMatchers("/v1/**").permitAll()
.accessDecisionManager(accessDecisionManager());
}
@Bean
public AccessDecisionManager accessDecisionManager() {
List collect = menuService
.listOpen()
.stream()
.map(m -> new UrlGrantedAuthority(m.getUrl()))
.collect(toList());
List> decisionVoters
= Arrays.asList(
new WebExpressionVoter(),
new UrlMatchVoter(collect));
return new UnanimousBased(decisionVoters);
}
/**
* 跨域请求配置
*
* @param properties 配置属性文件名
* @return
*/
@Bean
public CorsConfigurationSource corsConfigurationSource(CorsProperties properties) {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(properties.getAllowedOrigins());
configuration.setAllowedMethods(properties.getAllowedMethods());
configuration.setAllowCredentials(properties.getAllowCredentials());
configuration.setAllowedHeaders(properties.getAllowedHeaders());
configuration.setExposedHeaders(properties.getExposedHeaders());
configuration.setMaxAge(properties.getMaxAge());
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
拦截器:
public class InterceptorConfig implements HandlerInterceptor {
private static final Logger log = LoggerFactory.getLogger(InterceptorConfig.class);
/**
* 进入controller层之前拦截请求
*
* @param request
* @param response
* @param o
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
log.info("---------------------开始进入请求地址拦截----------------------------");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
然后是采坑的地方,这里必须重写WebMvcConfigurer的addCorsMappings(CorsRegistry corsRegistry)方法才能彻底解决问题(既能swagger访问后端接口,也能解决前端跨域问题),
WebAppConfig:
@Component
public class WebAppConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册自定义拦截器,添加拦截路径和排除拦截路径
registry.addInterceptor(new InterceptorConfig()).addPathPatterns("/v1/**/**");
}
/**
* 解决security下跨域失效问题,
*/
@Override
public void addCorsMappings(CorsRegistry corsRegistry) {
corsRegistry.addMapping("/**")
// 放行哪些原始域
.allowedOrigins("*")
// 是否发送cookie
.allowCredentials(true)
// 放行哪些请求
.allowedMethods("GET", "POST", "OPTIONS", "DELETE", "PUT")
// 放行哪些header
.allowedHeaders("*")
// 暴露哪些头部信息(因为跨域访问默认不能获取全部header
.exposedHeaders("Header1", "Header2");
}
}
参考了这位老哥的博客http://www.jetchen.cn/spring-security-cors/#comment-450 ,但是不用按照他那样配置过滤器也可以,可以参考参考
这样配置之后就可以解决,但是记得生产环境一定要关闭swagger:
关闭之后 访问swagger就是这样了:
开发的时候,改为true就可以了,可以通过读取yml配置,灵活的切换
如有问题,欢迎留言交流讨论