跨域原因产生:
其实就是浏览器安全机制,在当前域名请求网站中,默认不允许通过ajax请求发送其他域名,浏览器会无法返回结果。
也就是a项目页面访问b项目服务:
前端代码(a项目的前端):
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>B网站访问</title>
</head>
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
type : "GET",
async : false,
url : "http://a.a.com/a/FromServlet?userName=644064",
dataType : "json",
success : function(data) {
alert(data["userName"]);
},
error : function() {
alert('fail');
}
});
});
</script>
<body>
<img alt="" src="http://a.a.com/a/imgs/log.png">
</body>
</html></script>
<body>
<img alt="" src="http://a.a.com/a/imgs/log.png">
</body>
</html>
后端(b项目):
@WebServlet("/FromServlet")
public class FromServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String userName = req.getParameter("userName");
JSONObject jsonObject = new JSONObject();
jsonObject.put("userName", userName);
resp.getWriter().println(jsonObject.toJSONString());
}
}
解决方式:
1 jsonp 只支持get请求,不支持post请求
$.ajax({ type : "GET", async : false, url : "http://a.a.com/a/FromUserServlet?userName=张三", dataType : "jsonp",//数据类型为jsonp jsonp : "jsonpCallback",//服务端用于接收callback调用的function名的参数 success : function(data) { alert(data.result); }, error : function() { alert('fail'); } }); |
在b项目后端代码中加上:
String jsonpCallback = req.getParameter("jsonpCallback");
out.println(jsonpCallback + "(" + resultJSON.toJSONString() + ")");// 并将jsonp带回去
原理:使用script发送get请求,传参数(jsonpCallback)回调再带回来进行解析
2 使用接口网关---nginx搭建企业级api接口网关,springZULL接口网关 (最优解)
通过nginx进行转发,域名相同,具体项目名不同
3 httpClient内部转发
缺点:效率低,进行两次请求,一次是本b项目请求b后端,一次是b后端请求a后端,浪费资源,不存在跨域问题
优点:安全,抓包分析不到
package com.itmayiedu;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
@WebServlet("/ToFromServlet")
public class ToFromServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 创建默认连接
CloseableHttpClient httpClient = HttpClients.createDefault();
// 创建get请求
HttpGet httpGet = new HttpGet("http://a.a.com/a/FromServlet?userName=" + req.getParameter("userName"));
CloseableHttpResponse response = httpClient.execute(httpGet);
int code = response.getStatusLine().getStatusCode();
// 获取状态
System.out.println("http请求结果:" + code);
if (code == 200) {
String result = EntityUtils.toString(response.getEntity());
System.out.println(result);
resp.getWriter().print(result);
response.close();
httpClient.close();
// 将string转换html框架
}
}
}
4 添加header请求头,允许跨域访问
CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法
在b项目后端代码中加一句 req.setHeader("Access-Control-Allow-Origin", "*"); 表示浏览器允许跨域访问,*号表示允许所有的域名,一般来说,这种方式在项目中采用CORS(跨域资源共享)来配置,他是一种网络浏览器的技术规范,它为Web服务器定义了一种方式,允许网页从不同的域访问其资源。
@Component
public class CorsFilter implements Filter {
final static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CorsFilter.class);
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest reqs = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", reqs.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "GET,POST, PUT, DELETE");
response.setHeader("Access-Control-Allow-Headers", "Authorization,Origin, X-Requested-With, Content-Type, Accept,Access-Token");
response.setHeader("Access-Control-Expose-Headers", "*");
chain.doFilter(req, res);
}
@Override
public void init(FilterConfig filterConfig) {}
@Override
public void destroy() {}
}
表单重复提交解决方案:
场景模拟:界面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Form表单</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/DoFormServlet" method="post">
用户名:<input type="text" name="userName">
<input type="submit" value="提交" id="submit">
</form>
</body>
</html>
代码:
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/DoFormServlet")
public class DoFormServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String userName = req.getParameter("userName");
try {
Thread.sleep(300);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("往数据库插入数据...."+userName);
resp.getWriter().write("success");
}
}
重复提交原因:网络延迟,重复点击提交,刷新页面
解决办法:
前端使用javascript 解决
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Form表单</title>
<script type="text/javascript">
var isFlag = false; //表单是否已经提交标识,默认为false
function submitFlag() {
if (!isFlag) {
isFlag = true;
return true;
} else {
return false;
}
}
</script>
</head>
<body>
<form action="${pageContext.request.contextPath}/DoFormServlet"
method="post" onsubmit="return submitFlag()">
用户名:<input type="text" name="userName"> <input type="submit"
value="提交" id="submit">
</form>
</body>
</html>
还有一种方式:
function dosubmit(){ //获取表单提交按钮 var btnSubmit = document.getElementById("submit"); //将表单提交按钮设置为不可用,这样就可以避免用户再次点击提交按钮 btnSubmit.disabled= "disabled"; //返回true让表单可以正常提交 return true; } |
后端使用token:
如果客户端无法解决,那么就在服务器端解决,在服务器端解决就需要用到session了。
具体的做法:在服务器端生成一个唯一的随机标识号,专业术语称为Token(令牌),同时在当前用户的Session域中保存这个Token。然后将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token,表单提交的时候连同这个Token一起提交到服务器端,然后在服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。
在下列情况下,服务器程序将拒绝处理用户提交的表单请求:
- 存储Session域中的Token(令牌)与表单提交的Token(令牌)不同。
- 当前用户的Session中不存在Token(令牌)。
- 用户提交的表单数据中没有Token(令牌)。
转发代码:
@WebServlet("/ForwardServlet")
public class ForwardServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getSession().setAttribute("sesionToken", TokenUtils.getToken());
req.getRequestDispatcher("form.jsp").forward(req, resp);
}
}
前端:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Form表单</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/DoFormServlet"
method="post" onsubmit="return dosubmit()">
<input type="hidden" name="token" value="${sesionToken}"> 用户名:<input type="text"
name="userName"> <input type="submit" value="提交" id="submit">
</form>
</body>
</html>
后端:
@WebServlet("/DoFormServlet")
public class DoFormServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
boolean flag = isFlag(req);
if (!flag) {
resp.getWriter().write("已经提交...");
System.out.println("数据已经提交了..");
return;
}
String userName = req.getParameter("userName");
try {
Thread.sleep(300);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("往数据库插入数据...." + userName);
resp.getWriter().write("success");
}
public boolean isFlag(HttpServletRequest request) {
HttpSession session = request.getSession();
String sesionToken = (String) session.getAttribute("sesionToken");
String token = request.getParameter("token");
if (!(token.equals(sesionToken))) {
return false;
}
session.removeAttribute("sesionToken");
return true;
}
}