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

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

前端:http://localhost:5173

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

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

package com.example.demo.config;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private UsersService usersService;

    @Autowired
    VerificationCodeFilter verificationCodeFilter;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(password());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/assets/**","/js/**","/css/**","/img/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(verificationCodeFilter, UsernamePasswordAuthenticationFilter.class);
        http.authorizeRequests()
                .antMatchers("/sms/system/getVerifiCodeImage", "/v2/api-docs", "/swagger-resources/configuration/ui",
                        "/swagger-resources", "/swagger-resources/configuration/security",
                        "/swagger-ui.html", "/webjars/**").permitAll()
                .anyRequest().authenticated()
                .and()

                // 表单登录
                .formLogin()
                // 登录成功处理(对应流程2)
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        Map<String, Object> map = new HashMap<String,Object>();
                        String token = usersService.GenerateTokenByUserName(((User)authentication.getPrincipal()).getUsername());
                        map.put("token", token);
                        response.setContentType("application/json;charset=utf-8");
                        response.getWriter().write(
                                JSON.toJSONString(Result.ok(map)));
                    }
                })
                // 登录失败处理(对应流程3)
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        response.getWriter().write(
                                JSON.toJSONString(Result.fail().message("登录失败")));
                    }
                })
                .and()
                //登出
                .logout()
                //登出成功处理(对应流程4)
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        response.getWriter().write(
                                JSON.toJSONString(Result.ok()));
                    }
                })
                .and()
                //异常处理
                .exceptionHandling()
                //未登录处理(对应流程1)
                .authenticationEntryPoint(new AuthenticationEntryPoint() {
                    @Override
                    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        response.getWriter().write(JSON.toJSONString(Result.fail()));
                    }
                })
                //没有权限处理(对应流程5)
                .accessDeniedHandler(new AccessDeniedHandler() {
                    @Override
                    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        response.getWriter().write(JSON.toJSONString(Result.fail()));
                    }
                })
                .and().cors().configurationSource(corsConfigurationSource())
                .and()
                .csrf().disable();
    }


    @Bean
    PasswordEncoder password(){return new BCryptPasswordEncoder();}

    @Bean
    CorsConfigurationSource corsConfigurationSource(){
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // 允许跨域访问的站点
        corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:5173"));
        //允许跨域访问的methods
        corsConfiguration.setAllowedMethods(Arrays.asList("GET","POST"));
        // 允许携带凭证
        corsConfiguration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        //对所有URL生效
        source.registerCorsConfiguration("/**",corsConfiguration);
        return source;
    }
}

VerificationCodeFilter:

package com.example.demo.Filter;


//验证码校验
@Component
public class VerificationCodeFilter extends GenericFilter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        if("POST".equals(request.getMethod()) && "/login".equals(request.getServletPath()))
        {
            HttpSession session = request.getSession();

            String sessionVerifiCode = (String)session.getAttribute("verifiCode");
            String loginVerifiCode = request.getParameter("verifiCode");

            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();

            if(sessionVerifiCode.equals("")||sessionVerifiCode==null)
            {
                out.write(JSON.toJSONString(Result.fail().message("验证码失效,请刷新后重试")));
                out.flush();
                out.close();
                return;
            }
            else if(!sessionVerifiCode.equalsIgnoreCase(loginVerifiCode))
            {
                out.write(JSON.toJSONString(Result.fail().message("验证码有误,请小心输入后重试")));
                out.flush();
                out.close();
                return;
            }
            else
            {
                session.removeAttribute("verifiCode");
                filterChain.doFilter(request,response);
            }

        }
        else
        {
            filterChain.doFilter(request,response);
        }
    }
}

MyUserDetailsServices:

package com.example.demo.service.Impl;

@Service("userDetailsService")
public class MyUserDetailsServices implements UserDetailsService {
    @Autowired
    private UsersMapper usersMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<Users> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username);
        Users users = usersMapper.selectOne(wrapper);

        if(users == null)
        {
            throw new UsernameNotFoundException("用户名不存在");
        }

        List<GrantedAuthority> auths =
                AuthorityUtils.commaSeparatedStringToAuthorityList("role");

        return new User(users.getUsername(),
                new BCryptPasswordEncoder().encode(users.getPassword()), auths);
    }
}

前端部分代码:

axios({
          method: 'post',
          url: 'http://localhost:9001/login',
          headers:{'content-type': 'application/x-www-form-urlencoded'},
          data: qs.stringify({
              username: loginform.username,
              password: loginform.userpassword,
              verifiCode: loginform.verifycode,
              userType: loginform.usertype
          })
          }).then(response => {
              console.log(response);
              if(response.data.code == 200 && response.data.ok)
              {
                  sessionStorage.setItem("token",response.data.data.token)
                  router.push('/dashboard')
              }
              else{
                  ElMessage({
                      showClose: true,
                      message: response.data.message,
                      type: 'warning',
                      duration : 1000
                  });
                  refreshVerifyCode();
              }
          }).catch(function (error) {
              console.log(error);
              refreshVerifyCode();
      });

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

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

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

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

打开vite.config.js文件,

defineConfig里添加如下代码:

server:
    {
      proxy:{
        '/api':{
          target:'http://localhost:9001',
          changeOrigin:true,
          rewrite: (path)=>path.replace(/^\/api/,'')
        }
      }
    }

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

axios({
          method: 'post',
          url: '/api/login',

然后,问题解决了~~~

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

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

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

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

corsConfiguration.addAllowedHeader("token");

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值