背景介绍

在最近的一个项目中,前端采用vue + element-ui,后端使用springboot,整合了jwt以及spring security来实现安全校验,同时引入swagger进行前后端api测试调用。

遇到的坑

  1. 将前端项目npm run build后生成的项目放在后端springboot项目resouces/static/目录下,启动项目后无法访问静态资源,包括hmtl、js、css等文件
  2. swagger无法访问,页面无法展示

相关代码

1
2
3
4
5
6
// WebMvcConfig
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {

// 省略...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// WebSecurityConfig
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

//省略一些代码
@Override
protected void configure(final HttpSecurity http)
throws Exception {
http // 关闭cors
.cors().disable()
// 关闭csrf
.csrf().disable()
// 无状态Session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 异常处理
.exceptionHandling().authenticationEntryPoint(this.jwtAuthenticationEntryPoint).and()
// 对所有的请求都做权限校验
.authorizeRequests()
// 允許访问静态资源
.antMatchers("/",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js")
.permitAll()
// 允许登录和注册
.antMatchers(
HttpMethod.POST,
"/api/user/login"
).permitAll()

// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated().and();

http // 基于定制JWT安全过滤器
.addFilterBefore(this.jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
// 禁用页面缓存
http.headers().cacheControl();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// JwtAuthenticationFilter
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

private final static Logger log = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
@Resource
private JwtUtil jwtUtil;

@Override
protected void doFilterInternal(@Nonnull final HttpServletRequest request, @Nonnull final HttpServletResponse response, @Nonnull final FilterChain filterChain)
throws ServletException, IOException {

// 解决跨域问题
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Content-Length, Authorization, Accept, X-Requested-With");
// 明确允许通过的方法,不建议使用*
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Expose-Headers", "*");

// 省略其他的一些代码

filterChain.doFilter(request, response);
}
}

注意,这里解决跨域问题还有其他的方法,有空的时候会整理一下springboot解决跨域问题的方案。

原因分析

来看下spring boot里是怎么实现对src/main/resources/static这些目录的支持
springboot主要是通过org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration来实现的

1
2
3
4
5
6
7
8
9
10
11
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
//省略....
}

注意到 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)这个注解,当存在WebMvcConfigurationSupport这个类的时候,WebMvcAutoConfiguration这个自动配置就会被略去,不执行,导致springboot的mvc配置失效,无法访问到静态资源。

解决方法

  1. 在WebMvcConfig类中,将extends WebMvcConfigurationSupport改为implements WebMvcConfigurer
  2. 在WebSecurityConfig类中,允许访问swagger相关的资源
    1
    2
    3
    4
    http
    .authorizeRequests()
    .antMatchers("/v2/api-docs", "/configuration/**", "/swagger*/**", "/webjars/**")
    .permitAll();

Read more

有关@EnableWebMvc、WebMvcConfigurationSupport、WebMvcConfigurer的相关信息,可以阅读如下资料:
(WebMvcConfigurerAdapter已被deprecated)
[1] confuse between WebMvcConfigurationSupport and WebMvcConfigurerAdapter
[2] EnableWebMvc vs WebMvcConfigurationSupport
[3] 解析@EnableWebMvc 、WebMvcConfigurationSupport和WebMvcConfigurationAdapter
[4] SpringBoot2.x中WebMvcConfigurerAdapter已过时,使用WebMvcConfigurationSupport替换时自动配置失效
[5] Spring MVC - Importing configurations with @EnableWebMvc
[6] 深入Spring Boot:显式配置 @EnableWebMvc 导致静态资源访问失败
[7] WebMvcConfigurerAdapter 在Spring5.0已被废弃

Swagger方面:
[1] Swagger and Spring Security
[2] SpringBoot+spring Security+JWT整合swagger
[3] Spring Boot + Spring Security + JWT 集成Swagger文档问题