多系统,单一位置登陆,实现多系统同时登陆的一种技术java
(三方登陆:某系统使用其余系统的用户,实现本系统登陆的方式。如微信登陆、支付宝登陆)nginx
单点登陆通常是用于互相授信的系统,实现单一位置登陆,全系统有效web
所谓 Session 跨域就是摒弃了系统提供的 Session ,而使用自定义的相似 Session 的机制来保存客户端数据的一种解决方案。算法
如:经过设置 cookie 的 domain 来实现 cookie 的跨域传递。在 cookie 中传递一个自定义的 session_id。这个 session_id 是客户端的惟一标记,将这个标记做为key,将客户须要保存的数据做为value,在服务端进行保存(数据库保存或nosql保存)。这种机制就是 Session 的跨域解决。spring
什么为跨域:客户端请求的时候,请求的服务器,不是同一个IP、端口、域名、主机名的时候,都称为跨域。sql
什么是域:在应用模型中,一个完整的、有独立访问路径的功能集合成为一个域。数据库
如:百度称为一个应用或系统,其下有若干个域,如搜索引擎(www.baidu.com),百度贴吧(tie.baidu.com),百度知道(zhidao.baidu.com)等。json
有时也称为多级域名。域的划分:以IP、端口、域名、主机名为标准,实现划分。后端
spring-session 技术是 spring 提供的用于处理集群会话共享的解决方案。spring-session技术是将用户 session 数据保存到第三方容器中,如数据库。跨域
Spring-session 技术是解决同域名下的多服务器集群 session 共享问题的,不能解决跨域 Session 共享问题
nginx中的 ip_hash 技术可以将某个 ip 的请求定向到同一台后端,这样一来这个ip下的某个客户端和某个后端就能创建起稳固的session,ip_hash是在upstream配置中定义的
使用基于 Token 的身份验证方法,在服务端不须要存储用户的登陆记录,大概流程以下:
1)客户端使用用户名、密码请求登陆
2)服务端收到请求、去验证用户名与密码
3)验证成功后,服务端会签发y一个 token ,再把这个 token 发送给客户端
4)客户端收到 token 之后能够把它存储起来,好比放在 cookie 里或者 Local Storage里
5)客户端每次向服务器请求资源的时候须要带着服务器签发的 token
6)服务端收到请求,而后去验证客户端请求里面带着的 token,若是验证成功,就向客户端返回请求的数据
使用token的优点:
无状态、可扩展:
在客户端存储的 token 是无状态的,而且可以被扩展,基于这种无状态和不存储session信息,负载均衡器可以将用户信息从一个服务传到其余服务器上。
安全性:
请求中发送token而再也不发送cookie可以防止CSRF(跨域请求伪造)。即便在客户端使用cookie存储token。cookie也仅仅是一个存储机制而不是用于认证。
不将信息存储在session中,让咱们少了对session的操做。
JWT是一种紧凑且自包含的,用于在多方传递 json 对象的技术。传递的数据可使用数字签名增长其安全性。可使用HMAC加密算法或RSA公钥/私钥加密方式。
紧凑:数据小,能够经过URL、POST参数,请求头发送,且数据小表明传输速度快。
自包含:使用 payload 数据块j记录用户必要且不隐私的数据,能够有效的减小数据库访问次数,提升代码性能
JWT通常用于处理用户身份校验或数据信息交换
JWT的数据结构:A.B.C 以.(点)来划分
A-header 头信息
B-payload (有效荷载?)
C-Signature 签名
数据结构:{"alg":"加密算法名称","typ":"JWT"}
alg能够有 HMAC 或 SHA256 或 RSA 等
签名信息,这是一个由开发者提供的信息。是服务器验证的传递的数据是否有效安全的标准。
package cn.zytao.taosir; import java.util.HashMap; import java.util.Map; /** * 用于模拟用户数据的,开发中应访问数据库验证用户 * @author TAOSIR * */ public class JWTUsers { private static final Map<String,String> USERS =new HashMap<>(11); static { for(int i=0;i<10;i++) { USERS.put("admin"+i, "pwd"+i); } } //验证是否能够登陆 public static boolean isLogin(String username,String pwd) { if(null == username || username.trim().length()==0) return false; String obj=USERS.get(username); if(null ==obj||!obj.equals(pwd)) return false; return true; } }
package cn.zytao.taosir; /** * 做为Subject数据使用,也就是payload中保存的public claims * 其中不该包含任何敏感数据 * 开发中建议使用实体类型,或BO,DTO数据对象 * @author TAOSIR * */ public class JWTSubject { private String username; public JWTSubject() { super(); } public JWTSubject(String username) { super(); this.username = username; } public String getUsername() { return username; } public void setUsername(String username) { this.username=username; } }
package cn.zytao.taosir; /** * 做为Subject数据使用,也就是payload中保存的public claims * 其中不该包含任何敏感数据 * 开发中建议使用实体类型,或BO,DTO数据对象 * @author TAOSIR * */ public class JWTSubject { private String username; public JWTSubject() { super(); } public JWTSubject(String username) { super(); this.username = username; } public String getUsername() { return username; } public void setUsername(String username) { this.username=username; } }
package cn.zytao.taosir; public class JWTResponseData { private Integer code;//返回码 private Object data;//业务数据 private String msg;//返回描述 private String token;//身份标识 public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public String getToken() { return token; } public void setToken(String token) { this.token = token; } }
package cn.zytao.taosir; /** * JWT工具类 * @author TAOSIR * */ import java.util.Date; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import com.fasterxml.jackson.databind.ObjectMapper; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureException; public class JWTUtils { private static final String JWT_SECERT = "test_jwt_secert";//服务器的key,密钥 private static final ObjectMapper MAPPER = new ObjectMapper();//用户java对象和json字符串转换 public static final int JWT_ERRCODE_EXPIRE = 1005;//Token过时 public static final int JWT_ERRCODE_FAIL = 1006;//验证不经过 public static SecretKey generalKey() { try { byte[] encodedKey=JWT_SECERT.getBytes("UTF-8"); SecretKey key=new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES"); return key; } catch (Exception e) { e.printStackTrace(); return null; } } /** * 签发JWT,即建立token的方法 * @param id jwt的惟一身份标识,主要用来做为一次性token,从而回避重放攻击 * @param iss jwt签发者 * @param subject jwt所面向的用户,payload中记录的public,claims,当前环境中就是用户的登陆名 * @param ttlMills 有效期,单位毫秒 * @return */ public static String createJWT(String id,String iss,String subject,long ttlMillis) { //加密算法 SignatureAlgorithm signatureAlgorithm=SignatureAlgorithm.HS256; long nowMillis = System.currentTimeMillis(); Date now=new Date(nowMillis); SecretKey secretKey=generalKey(); //建立JWT的构造器用于生成token JwtBuilder builder=Jwts.builder() .setId(id) .setIssuer(iss) .setSubject(subject) .setIssuedAt(now) .signWith(signatureAlgorithm, secretKey); if(ttlMillis >= 0) { long expMillis =nowMillis+ttlMillis; Date exDate = new Date(expMillis); builder.setExpiration(exDate); } return builder.compact(); } /** * 验证JWT * @param jwtStr * @return */ public static JWTResult validateJWT(String jwtStr) { JWTResult checkResult=new JWTResult(); Claims claims=null; try { claims=parseJWT(jwtStr); checkResult.setSuccess(true); checkResult.setClaims(claims); } catch (ExpiredJwtException e) { checkResult.setSuccess(false); checkResult.setErrCode(JWT_ERRCODE_EXPIRE); } catch (SignatureException e) { checkResult.setSuccess(true); checkResult.setErrCode(JWT_ERRCODE_FAIL); } return checkResult; } /** * 解析JWT字符串 * @param jwt 就是token * @return */ public static Claims parseJWT(String jwt) { SecretKey secretKey=generalKey(); //getBody获取值就是token中记录的payload数据,就是其中保存的claims return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody(); } /** * 生成subject信息 * @param subObj * @return */ public static String generalSubject(Object subObj) { try { return MAPPER.writeValueAsString(subObj); } catch (Exception e) { e.printStackTrace(); return null; } } }
package cn.zytao.taosir.controller; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import cn.zytao.taosir.JWTResponseData; import cn.zytao.taosir.JWTResult; import cn.zytao.taosir.JWTSubject; import cn.zytao.taosir.JWTUsers; import cn.zytao.taosir.JWTUtils; @RestController public class JWTController { @RequestMapping("testAll") public Object testAll(HttpServletRequest request) { String token=request.getHeader("Authorization"); JWTResult result=JWTUtils.validateJWT(token); JWTResponseData responseData=new JWTResponseData(); if(result.isSuccess()) { responseData.setCode(200); responseData.setData(result.getClaims().getSubject()); String newToken=JWTUtils.createJWT(result.getClaims().getId(), result.getClaims().getIssuer(), result.getClaims().getSubject(), 1*60*1000); responseData.setToken(newToken); return responseData; }else { responseData.setCode(500); responseData.setMsg("用户未登陆"); return responseData; } } @RequestMapping("login") public Object login(String username,String password) { JWTResponseData responseData=null; //认证用户信息 if(JWTUsers.isLogin(username, password)) { JWTSubject subject=new JWTSubject(username); String jwtToken = JWTUtils.createJWT(UUID.randomUUID().toString(),"sxt-test-jwt", JWTUtils.generalSubject(subject), 1*60*1000); responseData=new JWTResponseData(); responseData.setCode(200); responseData.setData(null); responseData.setMsg("登陆成功"); responseData.setToken(jwtToken); }else { responseData=new JWTResponseData(); responseData.setCode(500); responseData.setData(null); responseData.setMsg("登陆失败"); responseData.setToken(null); } return responseData; } }