有状态登陆(cookie+redis)
首先有一个建立一个responseBean的类,两个属性,状态码和存储的数据。
首先是登陆:
①controller调用相应的服务(service)通过用户输入的账号和密码到数据库进行查询
②如果没有则直接向controller返回一个状态码为404,数据为空的responsebean对象
③有的话,将对象存在redis中,key得包含一个uuid的信息,比如key为user:toke:uuid,存的值为查出来的用户对象(一般新建一个用户对象,只存用户名,不用存密码),设置一个有效期。
④向controller返回一个状态码为200,数据为uuid的responseBean对象。
⑤controller判断拿到的response对象。如果是404,则向客户端回复用户名或则密码错误,如果是200,那么new一个cookie,key可以为user-token,值为拿到的responseBean对象里边的uuid。
注意点:redis缓存存储登陆的对象,要设置有效期,一般时间为30分钟,controller设置cookie的时候,要注意以下
if(200){
Cookie cookie =new Cookie("user-token", (String) login.getData());
cookie.setPath("/");
cookie.setHttpOnly(true);
cookie.setDomain("**.com");
response.addCookie(cookie);
}
其中:cookie.setPath("/");可以被同一个cookie应用服务器的其他应用访问。setHttpOnly(true),这样设置后js脚本将无法读取到cookie信息,有效防止XSS攻击(Cross Site Scripting跨站脚本攻击);setDomain();解决跨域的问题,一般设为父域名,然后下面的所有子域名都能访问。
查看登陆状态:
@RequestMapping("login_status")
@CrossOrigin(origins = "*",allowCredentials = "true")
@ResponseBody
public ResponseBean checkLogin(@CookieValue(name = "user-token",required = false) String uuid){
//先判断客户端是否传cookie,有则去redis查看是否过期,没有过期,刷新生存时间,返回对象,否则算是没有登陆
if ((uuid!=null)) {
return userService.checkLogin(uuid);
}
return new ResponseBean("404", null);
}
首先判断是否携带"user-token"的cookie,没有的话,则没有登陆,有的话拿到uuid,调用service的相关方法查看"user:token:uuid"的key是否过期,过期则没有登陆,没有过期,则刷新有效期为30分钟,将user对象的相关信息返回给controller。
注销登陆:
controller拿到客户端传过来的uuid,通过uuid直接删除cookie
Cookie cookie=new Cookie("user-token", uuid);
cookie.setDomain("**.com");
cookie.setPath("/");
cookie.setHttpOnly(true);
cookie.setMaxAge(0);
response.addCookie(cookie);
return new ResponseBean("200", "成功");
同源策略的问题:(协议、域名、端口一致)
问题,在首页系统,获取登陆认证状态,因为首页系统和sso系统是两个系统,如果这样写,请求能发出到登陆系统,但是收不到回应,因为ajax无法访问跨域的资源,基于同源策略的。
$.ajax({
url:"http://sso.**.com:9096/user/login_status",
success:function (data) {
if(data.statusCode==200){
alert("已登录")
}
}
})
解决办法1:jsonp
客户端页面
<script type="text/javascript">
function deal(data){
console.log(data);
}
</script>
<!--jsonp-->
<script src="http://sso.**.com:9096/user/login_status?callback=deal"></script>
服务端:
@RequestMapping("login_status")
@ResponseBody
public String checkIsLoginJsonp(@CookieValue(name = "user_token",required = false) String uuid,
String callback) throws JsonProcessingException {
ResultBean resultBean = null;
if(uuid != null){
resultBean = userService.checkIsLogin(uuid);
}else{
//3.返回找不到的结果
resultBean = new ResultBean("404", null);
}
//将对象转换为json
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(resultBean);
//回调客户端的方法
return callback+"("+json+")";//deal(json) padding
}
客户端的另一种编写方式,此时服务端不要做任何改变。
//那么当从服务器接收到数据时,实际上是用了<script>标签而不是XMLHttpRequest对象。
$.ajax({
url:"http://sso.**.com:9096/user/login_status",
jsonp:"callback",
jsonpCallback:"deal",
dataType:"jsonp"
})
此时不再是XmlHttpRequest对象了 ,仍然是script标签。
另一种,通过ajax(xhr)实现跨域。客户端的编写
$.ajax({
url:"http://sso.**.com:9096/user/login_status",
crossDomain:true,
xhrFields:{withCredentials:true},
success:function (data) {
console.log(data);
}
})
服务端的编写
@CrossOrigin(origins = "*",allowCredentials = "true")
注意:
- crossDomain 跨域
- 跨源请求默认不提供凭据(cookie、HTTP认证及客户端SSL证明等),通过设置withCredentials:true,可以实现携带cookie等信息。
- springmvc 4.2以上版本支持 @CrossOrigin 注解,实现跨域,allowCredentials指的是接收cookie的凭据参数。
无状态登录(cookie+jwt)
JSON Web Token是什么?
https://jwt.io/introduction/
官网描述:jwt是一套开放的标准(RFC 7519),它定制了一套简洁且URL安全的方案,以安全地在客户端和服务器端传输JSON格式的信息。
JWT格式
jwt有三个部分组成,Header,Payload,和Signature,格式为:header.Payload.Signature.
Header
header:{
"typ":"JWT",
"alg":"HS256"
}
header存放的内容说明编码对象时一个JWT和使用“SHA-256”的算法进行加密,加密生成Signature.
Claim(编码之后得到Payload)
{
"iss":"Issuer —— 用于说明该JWT是由谁签发的",
"sub":"Subject —— 用于说明该JWT面向的对象",
"aud":"Audience —— 用于说明该JWT发送给的用户",
"exp":"Expiration Time —— 数字类型,说明该JWT过期的时间",
"nbf":"Not Before —— 数字类型,说明在该时间之前JWT不能被接受与处理",
"iat":"Issued At —— 数字类型,说明该JWT何时被签发",
"jti":"JWT ID —— 说明标明JWT的唯一ID",
"user-definde1":"自定义属性举例",
"user-definde2":"自定义属性举例"
}
claim里边的都是可选的。里边可以存放一些自定义属性,在用户认证用于表明一个用户的身份,claim经过Base64编码得到Payload。
Signature
用上面定义的算法一个存放在服务端的密钥对Header.Payload进行加密,得到Signature.
服务端验证
客户端将token通过cookie到达服务端,服务端会对Header进行解码得到加密的算法,然后通过存放在服务器端的密钥对Headler.Payload这个字符串进行加密,对比JWT中的Signature以验证token是否有效,完成身份的验证,通过验证才会使用payload的数据。
登陆验证流程