当前位置:   article > 正文

Vue3+SpringSecurity,用代理服务器解决头疼的跨域问题_spring-security+vue3

spring-security+vue3

本人的前后端项目是这样的:

前端:http://localhost:5173

后端:  http://localhost:9001,并且使用了spring security,配置了允许跨域,而且可以实现前后端分离登录。

后端部分代码如下(有的代码是参考了别的大神)

  1. package com.example.demo.config;
  2. @Configuration
  3. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  4. @Autowired
  5. private UserDetailsService userDetailsService;
  6. @Autowired
  7. private UsersService usersService;
  8. @Autowired
  9. VerificationCodeFilter verificationCodeFilter;
  10. @Override
  11. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  12. auth.userDetailsService(userDetailsService).passwordEncoder(password());
  13. }
  14. @Override
  15. public void configure(WebSecurity web) throws Exception {
  16. web.ignoring().antMatchers("/assets/**","/js/**","/css/**","/img/**");
  17. }
  18. @Override
  19. protected void configure(HttpSecurity http) throws Exception {
  20. http.addFilterBefore(verificationCodeFilter, UsernamePasswordAuthenticationFilter.class);
  21. http.authorizeRequests()
  22. .antMatchers("/sms/system/getVerifiCodeImage", "/v2/api-docs", "/swagger-resources/configuration/ui",
  23. "/swagger-resources", "/swagger-resources/configuration/security",
  24. "/swagger-ui.html", "/webjars/**").permitAll()
  25. .anyRequest().authenticated()
  26. .and()
  27. // 表单登录
  28. .formLogin()
  29. // 登录成功处理(对应流程2)
  30. .successHandler(new AuthenticationSuccessHandler() {
  31. @Override
  32. public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
  33. Map<String, Object> map = new HashMap<String,Object>();
  34. String token = usersService.GenerateTokenByUserName(((User)authentication.getPrincipal()).getUsername());
  35. map.put("token", token);
  36. response.setContentType("application/json;charset=utf-8");
  37. response.getWriter().write(
  38. JSON.toJSONString(Result.ok(map)));
  39. }
  40. })
  41. // 登录失败处理(对应流程3)
  42. .failureHandler(new AuthenticationFailureHandler() {
  43. @Override
  44. public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
  45. response.setContentType("application/json;charset=utf-8");
  46. response.getWriter().write(
  47. JSON.toJSONString(Result.fail().message("登录失败")));
  48. }
  49. })
  50. .and()
  51. //登出
  52. .logout()
  53. //登出成功处理(对应流程4)
  54. .logoutSuccessHandler(new LogoutSuccessHandler() {
  55. @Override
  56. public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
  57. response.setContentType("application/json;charset=utf-8");
  58. response.getWriter().write(
  59. JSON.toJSONString(Result.ok()));
  60. }
  61. })
  62. .and()
  63. //异常处理
  64. .exceptionHandling()
  65. //未登录处理(对应流程1)
  66. .authenticationEntryPoint(new AuthenticationEntryPoint() {
  67. @Override
  68. public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
  69. response.setContentType("application/json;charset=utf-8");
  70. response.getWriter().write(JSON.toJSONString(Result.fail()));
  71. }
  72. })
  73. //没有权限处理(对应流程5)
  74. .accessDeniedHandler(new AccessDeniedHandler() {
  75. @Override
  76. public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
  77. response.setContentType("application/json;charset=utf-8");
  78. response.getWriter().write(JSON.toJSONString(Result.fail()));
  79. }
  80. })
  81. .and().cors().configurationSource(corsConfigurationSource())
  82. .and()
  83. .csrf().disable();
  84. }
  85. @Bean
  86. PasswordEncoder password(){return new BCryptPasswordEncoder();}
  87. @Bean
  88. CorsConfigurationSource corsConfigurationSource(){
  89. CorsConfiguration corsConfiguration = new CorsConfiguration();
  90. // 允许跨域访问的站点
  91. corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:5173"));
  92. //允许跨域访问的methods
  93. corsConfiguration.setAllowedMethods(Arrays.asList("GET","POST"));
  94. // 允许携带凭证
  95. corsConfiguration.setAllowCredentials(true);
  96. UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
  97. //对所有URL生效
  98. source.registerCorsConfiguration("/**",corsConfiguration);
  99. return source;
  100. }
  101. }

VerificationCodeFilter:

  1. package com.example.demo.Filter;
  2. //验证码校验
  3. @Component
  4. public class VerificationCodeFilter extends GenericFilter {
  5. @Override
  6. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  7. HttpServletRequest request = (HttpServletRequest)servletRequest;
  8. HttpServletResponse response = (HttpServletResponse) servletResponse;
  9. if("POST".equals(request.getMethod()) && "/login".equals(request.getServletPath()))
  10. {
  11. HttpSession session = request.getSession();
  12. String sessionVerifiCode = (String)session.getAttribute("verifiCode");
  13. String loginVerifiCode = request.getParameter("verifiCode");
  14. response.setContentType("application/json;charset=utf-8");
  15. PrintWriter out = response.getWriter();
  16. if(sessionVerifiCode.equals("")||sessionVerifiCode==null)
  17. {
  18. out.write(JSON.toJSONString(Result.fail().message("验证码失效,请刷新后重试")));
  19. out.flush();
  20. out.close();
  21. return;
  22. }
  23. else if(!sessionVerifiCode.equalsIgnoreCase(loginVerifiCode))
  24. {
  25. out.write(JSON.toJSONString(Result.fail().message("验证码有误,请小心输入后重试")));
  26. out.flush();
  27. out.close();
  28. return;
  29. }
  30. else
  31. {
  32. session.removeAttribute("verifiCode");
  33. filterChain.doFilter(request,response);
  34. }
  35. }
  36. else
  37. {
  38. filterChain.doFilter(request,response);
  39. }
  40. }
  41. }

MyUserDetailsServices:

  1. package com.example.demo.service.Impl;
  2. @Service("userDetailsService")
  3. public class MyUserDetailsServices implements UserDetailsService {
  4. @Autowired
  5. private UsersMapper usersMapper;
  6. @Override
  7. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  8. QueryWrapper<Users> wrapper = new QueryWrapper<>();
  9. wrapper.eq("username", username);
  10. Users users = usersMapper.selectOne(wrapper);
  11. if(users == null)
  12. {
  13. throw new UsernameNotFoundException("用户名不存在");
  14. }
  15. List<GrantedAuthority> auths =
  16. AuthorityUtils.commaSeparatedStringToAuthorityList("role");
  17. return new User(users.getUsername(),
  18. new BCryptPasswordEncoder().encode(users.getPassword()), auths);
  19. }
  20. }

前端部分代码:

  1. axios({
  2. method: 'post',
  3. url: 'http://localhost:9001/login',
  4. headers:{'content-type': 'application/x-www-form-urlencoded'},
  5. data: qs.stringify({
  6. username: loginform.username,
  7. password: loginform.userpassword,
  8. verifiCode: loginform.verifycode,
  9. userType: loginform.usertype
  10. })
  11. }).then(response => {
  12. console.log(response);
  13. if(response.data.code == 200 && response.data.ok)
  14. {
  15. sessionStorage.setItem("token",response.data.data.token)
  16. router.push('/dashboard')
  17. }
  18. else{
  19. ElMessage({
  20. showClose: true,
  21. message: response.data.message,
  22. type: 'warning',
  23. duration : 1000
  24. });
  25. refreshVerifyCode();
  26. }
  27. }).catch(function (error) {
  28. console.log(error);
  29. refreshVerifyCode();
  30. });

(因为我们这里是请求'/login'路径,可以通过浏览器直接访问这个URL,可以看到浏览器显示的是一个html页面,所以我们这里需要使用表单提交)

目前的问题是,登录时没有跨域问题,验证码也能正常显示。但是我一旦通过router跳转到其他vue文件,就出现跨域问题,如下图。

我试着给跳转后需要请求的controller添加@CrossOrigin,仍然解决不了。

抓狂之下,只有死马当活马医,用最后的办法,vue设置代理服务器:

打开vite.config.js文件,

defineConfig里添加如下代码:

  1. server:
  2. {
  3. proxy:{
  4. '/api':{
  5. target:'http://localhost:9001',
  6. changeOrigin:true,
  7. rewrite: (path)=>path.replace(/^\/api/,'')
  8. }
  9. }
  10. }

然后将前端所有的请求,从http://localhost:9001换成 api:

  1. axios({
  2. method: 'post',
  3. url: '/api/login',

然后,问题解决了~~~

如果你也遇到这操蛋的问题,希望这篇文章能帮助到你

注:后来发现是因为前端有通过header发送token给后端:

headers:{'token': sessionStorage.getItem("token")

所以后端只要添加如下代码也可以解决:

corsConfiguration.addAllowedHeader("token");

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/53923
推荐阅读
相关标签
  

闽ICP备14008679号