問題背景:
項目用的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的區別主要是:
- 這兩個都是繼承WebSecurityConfigurerAdapter後重寫的方法
- http.permitAll不會繞開springsecurity驗證,相當於是允許該路徑通過
- web.ignoring是直接繞開spring security的所有filter,直接跳過驗證
以下是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<UrlGrantedAuthority> collect = menuService
.listOpen()
.stream()
.map(m -> new UrlGrantedAuthority(m.getUrl()))
.collect(toList());
List<AccessDecisionVoter<? extends Object>> 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配置,靈活的切換
如有問題,歡迎留言交流討論