前后端分离项目存在的两个问题
1、登录状态如何保持
2、A与B两个服务端与同一个客户端交互如何确定这是同一个客户端
前言
本文名称解释
- 同源策略: 同源策略是浏览器保证安全的基础,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页同源;同源指的是同协议、同域名、同端口;
重点:前后端分离项目的登陆状态该如何保持
- 前后端统一在同一个服务中;用户登陆后台session存储用户信息、设置cookie并返回给浏览器,浏览器再次访问带上cookie,后台利用cookie获取用户信息,从而保持用户的登陆状态,这种机制被称为cookie-session机制;
- 同域下的前后端分离:常见形式我们利用nginx做反向代理,将前后端分离的项目有意识的做在同一域名下;
- 不同域下的前后端分离:当页面与后端服务不在同一域下,也就不满足同源策略;
这种情况有两种解决办法
A. JSONP解决跨域
在每个接口上增加callback参数
页面的访问进行改造@RequestMapping("test") public String setCookie(HttpServletResponse response,String callback){ Cookie cookie = new Cookie("test","same"); cookie.setPath("/"); response.addCookie(cookie); if (StringUtils.isNotBlank(callback)){ return callback+"('success')"; } return "success"; }
<script> $(function () { $.ajax({ url : "http://www.b.com:8888/test?callback=?", method: "get", dataType : 'jsonp', success : function (json) { console.log(json); } }); })
B. CORS解决跨域::以下方法是如何解决浏览器能跨域访问,但是如果想在这两个域中设置cookie等内容;火狐浏览器测试已经可以了,但是Google浏览器有限制,需要进行如下设置:
接口增加 : allowCredentials = “true” 或者 使用拦截器同一对接口进行处理
@CrossOrigin(origins="客户访问地址",allowCredentials = "true")
或者使用拦截器同一对接口进行处理-当客户访问地址满足我们配置的特殊地址时对这些请求设置允许跨域;最后的OPTIONS判断是在浏览器认为请求为复杂请求是会有预请求的操作,这个时候统一返回true告诉浏览器可以访问即可;
response.setHeader("Access-Control-Allow-Origin", originHeader);
response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers",
"Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token,Authorization");
response.setHeader("Access-Control-Allow-Credentials", "true");
if (((HttpServletRequest) req).getMethod().equals("OPTIONS")) {
return;
}
页面: withCredentials: true
<script>
$(function () {
$.ajax({
url : "http://www.b.com:8888/test",
method: "get",
success : function (json) {
console.log(json);
},
xhrFields: {
withCredentials: true
}
});
})
</script>
项目session回话管理
对于当后台只有一个项目时我们通过浏览器回话通过cookie取出session没有问题,但是当我们项目是多个服务a server和b server,如果页面当在a登陆后需要访问b服务提供的服务,那么b从session中找不到用户的登陆信息,那么b就会认为用户没有登陆;此时如何解决session的共享问题;
参考文章: https://cloud.tencent.com/developer/article/1143159
- 持久化session到数据库,即使用数据库来储存session。
- .使用redis共享session。是目前比较流行的解决方案
- 使用memcache同步session,memcache可以实现分布式,可将服务器中的内存组合起来,形成一>个“内存池”,以此充当公共空间,保存session信息
- 使用NFS共享session。NFS是Network File Server共享服务器的简称,最早由Sun公司为解决Unix网>络主机间的目录共享而研发。选择一台公共的NFS做共享服务器,储存所有session数据,每台服务器>所需的session均从此处获取
- 通过脚本或守护进程在多台服务器之间同步session
- 使用Cookie共享session。此方案可以说是独辟蹊径了,将分布式思想用到了极致。如上文分析所>说,session-cookie机制中,session与cookie相互关联,以cookie做中转站,用来找到对应的session,>其中session存放在服务器。那么如果将session中的内容存放在cookie中呢,那么则省略了服务器保存> session的过程,后台只需要根据cookie中约定的标识进行鉴权校验即可
项目token管理、jwt
JSON Web Token 是目前最流行的跨域身份验证解决方案;用起来更加灵活方便
jwt包含三个部分jwt头 + 有效数据 + 签名; 生成token返回给用户端,以后每次用户端上传这个token,后台利用他来识别用户; 签名可以防止客户端对token进行篡改;
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
public class JwtUtil {
/**
* key 与 salt自定义 ; param可为用户相关信息
* @param key
* @param param
* @param salt
* @return
*/
public static String encode(String key,Map<String,Object> param,String salt){
if(salt!=null){
key+=salt;
}
JwtBuilder jwtBuilder = Jwts.builder().signWith(SignatureAlgorithm.HS256,key);
jwtBuilder = jwtBuilder.setClaims(param);
String token = jwtBuilder.compact();
return token;
}
/**
* key 与 salt自定义 :与编码是保持一致即可;即可得到编码时的有效参数
* @param token
* @param key
* @param salt
* @return
*/
public static Map<String,Object> decode(String token ,String key,String salt){
Claims claims=null;
if (salt!=null){
key+=salt;
}
try {
claims= Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();
} catch ( JwtException e) {
return null;
}
return claims;
}
}