各种问题卡了一段时间,下面正文开始
前端
- 配置
axios
,使其允许携带Cookie
// 创建axios实例
const service = axios.create({
// api 的 base_url
baseURL: process.env.BASE_API,
// 请求超时时间
timeout: 60000,
//允许携带Cookie
withCredentials: true
})
- 跨域设置
dev: {
... ...
proxyTable: {
'/': {
//后端地址
target: 'http://localhost:9000',
changeOrigin: true,
pathRewrite: {
'^/': ''
}
}
},
... ...
}
后端配置
- 实现
WebMvcConfigurer
接口,重写addCorsMappings
方法,如下图所示
/**
* 添加跨域支持
*/
override fun addCorsMappings(registry: CorsRegistry) {
if ("dev".equals(env)) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("*")
.exposedHeaders("Access-Control-Allow-Origin:$vueHost",
"Access-Control-Allow-Headers:$vueHost",
"Authorization",
"X-Requested-With")
.maxAge(1728000)
}
}
vueHost
的值为前端的地址,如http://localhost:9090
,不要配置成*
,前端会有错误,类似于:
Failed to load http://localhost:9090/user/list: The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard ‘*’ when the request’s credentials mode is ‘include’. Origin ‘http://localhost:9000’ is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
- 继承
WebSecurityConfigurerAdapter
类,重写configure(HttpSecurity http)
方法,添加如下代码
@Override
protected void configure(HttpSecurity http) throws Exception {
AuthenticationSuccessHandler succHandler = (httpServletRequest, httpServletResponse, authentication) -> {
if ("dev".equals(env)) {
httpServletResponse.setHeader("Access-Control-Allow-Origin", vueHost);
}
this.writeResponseJson(httpServletResponse, new ObjectMapper().writeValueAsString(new MySecurityResponse(HttpCodeUtil.SUCCESS.getCode(), "", null)));
};
AuthenticationFailureHandler failHandler = (httpServletRequest, httpServletResponse, authenticationException) -> {
if ("dev".equals(env)) {
httpServletResponse.setHeader("Access-Control-Allow-Origin", vueHost);
}
this.writeResponseJson(httpServletResponse,
new ObjectMapper().writeValueAsString(new MySecurityResponse(HttpCodeUtil.FAILURE.getCode(), "用户名或密码错误", null)));
};
LogoutSuccessHandler logoutSuccHandler = (httpServletRequest, httpServletResponse, authentication) -> {
if ("dev".equals(env)) {
httpServletResponse.setHeader("Access-Control-Allow-Origin", vueHost);
}
this.writeResponseJson(httpServletResponse,
new ObjectMapper().writeValueAsString(new MySecurityResponse(HttpCodeUtil.SUCCESS.getCode(), "登出成功", null)));
};
if ("dev".equals(env)) {
http.cors().and().csrf().disable();
}
http
//所有的预请求都直接通过
.authorizeRequests()
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
... ...
.and()
.formLogin()
...
.successHandler(succHandler)
.failureHandler(failHandler)
.and()
.logout()
...
.logoutSuccessHandler(logoutSuccHandler)
//权限不足自定义处理器
.and()
.exceptionHandling().accessDeniedHandler(new MyAccessDeniedHandler())
- 实现
AccessDeniedHandler
接口的handle
方法:
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
logger.info("[Access denied] {} --- {}", request.getRequestURL(), SecurityContextHolder.getContext().getAuthentication());
logger.info("", response);
response.setContentType("application/json;charset=utf-8");
try (PrintWriter out = response.getWriter()) {
out.write(new ObjectMapper().writeValueAsString(new MySecurityResponse(HttpCodeUtil.ACCESSDENDIE.getCode(), HttpCodeUtil.ACCESSDENDIE.getCodeDesc(), null)));
} catch (IOException e) {
logger.error("MyAccessDeniedHandler: ", e);
}
}
此配置是为了在访问权限不足的资源时,直接返回一个302
状态的response
(这些也可以交给前端去维护。)