使用Shiro时的跨域问题探讨

6 篇文章 0 订阅

使用Shiro时的跨域问题探讨

作者:木子六日

时间:2021年1月13日

本来真的不想说这个问题,我感觉自己还没百分百弄明白,不过操作层面差不多了,又碰到了,索性说一下。

前后端分离的项目,后端和前端基本上是不会在同一台机子的同一个端口上的,这个时候就跨域了。

简单说就是你的页面要发另一个请求,这个请求的去处不是页面本身的来处,这就算跨域。

解决办法

简单地解决跨域我在之前的博客里都有写过,这里再贴一下吧。

传统的办法就是往响应头里加一些东西允许跨域:

package com.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;

public class AllowCORS implements HandlerInterceptor{
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type,Access-Token,Authorization,ybg");
		return true;
	}
}

这是SpringMVC拦截器的做法,其实也可以写一个filter。

SpringBoot的办法要更简单一些:

package com.ljj.SpringBootLog.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CORSConfigurer implements WebMvcConfigurer {

	@Override
	public void addCorsMappings(CorsRegistry registry) {
		registry.addMapping("/**").allowedOrigins("*").allowCredentials(true)
				.allowedMethods("GET", "POST", "DELETE", "PUT").maxAge(3600);
	}
}

会话保持

没错这就是我今天想说的。

ajax请求默认是不让带cookie的。如果我们希望带cookie,以axios为例:

import axios from 'axios'

export function request(config) {
	const instance = axios.create({
		baseURL: 'http://test',
		withCredentials: true
	})
	return instance(config)
}

我们就要加上这个withCredentials.

代码层面前端配这么一个东西就行了。

我们接着看后端,以SpringBoot为例:

package com.ljj.kongzi.config;


import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * CORS配置,允许跨域访问
 * @author muziliuri
 *
 */
@Configuration
public class CorsConfig implements WebMvcConfigurer{
	@Override
	public void addCorsMappings(CorsRegistry registry) {
		registry.addMapping("/**").allowedOrigins("http://localhost:8080").allowCredentials(true)
		.allowedMethods("GET", "POST", "DELETE", "PUT","HEAD","OPTIONS").maxAge(3600);
	}
}

origin处不能再写*了,必须写成具体的,就是你的前端服务的主机名端口号。

在开发过程中写成localhost就行了,即使后端的服务是在别的机子上也没关系

但是如果前后端的服务无法在同一台机子上,还会出现一个问题,就是你的浏览器认为这是不安全的。

  • Firefox是允许的;
  • Chrome是不允许的,输入chrome://flags,然后搜索samesite by default cookies,把那两项都disable了就行了。

最后部署前端服务的时候记得布到和后台一个机子上就行。

跨域时Shiro的重定向问题

上面的做法并不能保证万无一失,一旦会话断开,仍还会产生跨域问题。

为什么呢?我们知道Shiro在认证失败的时候会做重定向,前后端分离的时候一般是自定义一段json返回。

//认证失败
Map<String, Filter> filters=new HashMap<>();
filters.put("authc", new MyUserFilter());
shiroFilterFactoryBean.setFilters(filters);

其中MyUserFilter是我自定义的Filter:

package com.ljj.kongzi.shiro.filters;

import java.io.IOException;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.web.filter.authc.UserFilter;

import com.ljj.kongzi.common.Response;

public class MyUserFilter extends UserFilter{
	@Override
	protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
		HttpServletResponse resp = (HttpServletResponse) response;
        resp.setContentType("application/json;charset=utf-8");
		Response jsonResponse = new Response(600, "Please login first.", null);
		response.getWriter().write(jsonResponse.toJson());
	}

}

但是我发现只要代码过这儿,浏览器那边就一定会报跨域的错。

经过我的实验response的请求头里面并没有CorsConfig里面配置的信息,也就是说没有Access-Control-Allow-Origin这些。

也就是说对于shiro认证失败的请求,SpringBoot的CORS配置无效。

那只能手动配一下了:

package com.ljj.kongzi.shiro.filters;

import java.io.IOException;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.web.filter.authc.UserFilter;

import com.ljj.kongzi.common.Response;

public class MyUserFilter extends UserFilter{
	@Override
	protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
		HttpServletResponse resp = (HttpServletResponse) response;
		resp.setHeader("Access-Control-Allow-Origin", "http://localhost:8080");
		resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT, HEAD");
        resp.setHeader("Access-Control-Max-Age", "3600");
        resp.setHeader("Access-Control-Allow-Headers", "Content-Type,Access-Token,Authorization,ybg");
        resp.setHeader("Access-Control-Allow-Credentials", "true");
        resp.setContentType("application/json;charset=utf-8");
		Response jsonResponse = new Response(600, "Please login first.", null);
		response.getWriter().write(jsonResponse.toJson());
	}

}

这样一来就没问题了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值