初识单点登录及JWT实现
单点登录
多系统,单一位置登录,实现多系统同时登录的一种技术
(三方登录:某系统使用其他系统的用户,实现本系统登录的方式。如微信登录、支付宝登录)
单点登录一般是用于互相授信的系统,实现单一位置登录,全系统有效
一、Session跨域
所谓 Session 跨域就是摒弃了系统提供的 Session ,而使用自定义的类似 Session 的机制来保存客户端数据的一种解决方案。
如:通过设置 cookie 的 domain 来实现 cookie 的跨域传递。在 cookie 中传递一个自定义的 session_id。这个 session_id 是客户端的唯一标记,将这个标记作为key,将客户需要保存的数据作为value,在服务端进行保存(数据库保存或nosql保存)。这种机制就是 Session 的跨域解决。
什么为跨域:客户端请求的时候,请求的服务器,不是同一个IP、端口、域名、主机名的时候,都称为跨域。
什么是域:在应用模型中,一个完整的、有独立访问路径的功能集合成为一个域。
如:百度称为一个应用或系统,其下有若干个域,如搜索引擎(www.baidu.com),百度贴吧(tie.baidu.com),百度知道(zhidao.baidu.com)等。
有时也称为多级域名。域的划分:以IP、端口、域名、主机名为标准,实现划分。
二、Spring Session 共享
spring-session 技术是 spring 提供的用于处理集群会话共享的解决方案。spring-session技术是将用户 session 数据保存到第三方容器中,如数据库。
Spring-session 技术是解决同域名下的多服务器集群 session 共享问题的,不能解决跨域 Session 共享问题
三、Nginx Session 共享
nginx中的 ip_hash 技术能够将某个 ip 的请求定向到同一台后端,这样一来这个ip下的某个客户端和某个后端就能建立起稳固的session,ip_hash是在upstream配置中定义的
四、Token身份认证
使用基于 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的操作。
五、JSON Web Token(JWT)机制
JWT是一种紧凑且自包含的,用于在多方传递 json 对象的技术。传递的数据可以使用数字签名增加其安全性。可以使用HMAC加密算法或RSA公钥/私钥加密方式。
紧凑:数据小,可以通过URL、POST参数,请求头发送,且数据小代表传输速度快。
自包含:使用 payload 数据块j记录用户必要且不隐私的数据,可以有效的减少数据库访问次数,提高代码性能
JWT一般用于处理用户身份校验或数据信息交换
JWT的数据结构
JWT的数据结构:A.B.C 以.(点)来划分
A-header 头信息
B-payload (有效荷载?)
C-Signature 签名
header:
数据结构:{"alg":"加密算法名称","typ":"JWT"}
alg可以有 HMAC 或 SHA256 或 RSA 等
payload:主要分为三部分:已注册信息、公开数据、私有数据
singature:
签名信息,这是一个由开发者提供的信息。是服务器验证的传递的数据是否有效安全的标准。
执行流程

简单实现
1)造数据 JWTUsers模拟数据库用户名密码
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;
}
}
2)JWTSubject
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;
}
}
3)JWT结果对象
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;
}
}
4)响应对象
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;
}
}
5)JWT控制类
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;
}
}
}
6)写个简单的controller实践
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;
}
}
7)Postman查看情况


初识单点登录及JWT实现的更多相关文章
- CAS单点登录(一)——初识SSO
转载:https://blog.csdn.net/Anumbrella/article/details/80821486 一.初识CAS 首先我们来说一下CAS,CAS全称为Central Authe ...
- 看图理解JWT如何用于单点登录
单点登录是我比较喜欢的一个技术解决方案,一方面他能够提高产品使用的便利性,另一方面他分离了各个应用都需要的登录服务,对性能以及工作量都有好处.自从上次研究过JWT如何应用于会话管理,加之以前的项目中也 ...
- JWT(JSON Web Token) 多网站的单点登录,放弃session
多个网站之间的登录信息共享, 一种解决方案是基于cookie - session的登录认证方式,这种方式跨域比较复杂. 另一种替代方案是采用基于算法的认证方式, JWT(json web token) ...
- Spring Security构建Rest服务-1300-Spring Security OAuth开发APP认证框架之JWT实现单点登录
基于JWT实现SSO 在淘宝( https://www.taobao.com )上点击登录,已经跳到了 https://login.taobao.com,这是又一个服务器.只要在淘宝登录了,就能直接访 ...
- 170810、spring+springmvc+Interceptor+jwt+redis实现sso单点登录
在分布式环境中,如何支持PC.APP(ios.android)等多端的会话共享,这也是所有公司都需要的解决方案,用传统的session方式来解决,我想已经out了,我们是否可以找一个通用的方案,比如用 ...
- springboot+security+JWT实现单点登录
本次整合实现的目标:1.SSO单点登录2.基于角色和spring security注解的权限控制. 整合过程如下: 1.使用maven构建项目,加入先关依赖,pom.xml如下: <?xml v ...
- 基于JWT机制的单点登录
使用JWT实现单点登录时,需要注意token时效性.token是保存在客户端的令牌数据,如果永久有效,则有被劫持的可能.token在设计的时候,可以考虑一次性有效或一段时间内有效.如果设置有效时长,则 ...
- 手把手教你学会 基于JWT的单点登录
最近我们组要给负责的一个管理系统 A 集成另外一个系统 B,为了让用户使用更加便捷,避免多个系统重复登录,希望能够达到这样的效果--用户只需登录一次就能够在这两个系统中进行操作.很明显这就是单点登 ...
- Spring Security整合JWT,实现单点登录,So Easy~!
前面整理过一篇 SpringBoot Security前后端分离,登录退出等返回json数据,也就是用Spring Security,基于SpringBoot2.1.4 RELEASE前后端分离的情况 ...
随机推荐
- 浅谈urllib和requests
urllib和requests的学习 urllib requests 参考资料 urllib urllib是python的基本库之一,内置四大模块,即request,error,parse,robot ...
- Django入门--url路由基本配置
URL(Uniform Resoure Locater)统一资源定位符,是对可以从互联网上得到资源位置和访问方法的一种简洁形式,是互联网上标准资源的地址.互联网上的每个文件都有一个唯一的URL,它包含 ...
- 深入理解B/S与C/S架构
首先来介绍一下B/S与C/S架构 C/S架构简要介绍 在了解什么是B/S架构之前,我们有必要了解一下什么是C/S架构: C/S架构是第一种比较早的软件架构,主要用于局域网内.也叫 客户机/服务器模式. ...
- php 返回某个月的 每周几有几天
不得不承认,这真是一个奇葩的需求,无奈写个类凑活用用. 输入日期格式或者 时间戳,返回当月有多少个周一.周二.周三.....周日; 思路就是 找到这个月有多少天,在便利判断. 稍微考虑下闰年的情况 前 ...
- HDU 2439 The Mussels
The Mussels Time Limit: 1000ms Memory Limit: 32768KB This problem will be judged on HDU. Original ID ...
- mybatis逆向工程不生成Example
mybatis逆向生成映射文件时会生成一大堆example文件,没感觉有啥用,可以手动删除这些多余的东西,使项目变得好看许多 也可以通过配置达到目的: 原配置: <table tableName ...
- spring bean的作用域和自动装配
1 Bean的作用域 l singleton单列:整个容器中只有一个对象实例,每次去访问都是访问同一个对象 默认是单列 l prototype原型: 每次获取bean都产生一个新的对象,比如Ac ...
- mac下安装配置java开发环境
可以使用homebrew下载相关软件,以下具体讲一下环境的配置: mac下安装jdk vi .bash_profile 输入i,进入编辑模式 输入以下配置,其中JAVA_HOME是你的jdk安装目录 ...
- 微信小程序怎么获取当前页面的url
使用getCurrentPages可以获取当前加载中所有的页面对象的一个数组,数组最后一个就是当前页面. var pages = getCurrentPages() //获取加载的页面 var cur ...
- 使用GitHub来托管Larval框架
每个新框架都有自己的安装方法laravel 的安装方法有一下几种: (一) 通过下载 Laravel 包安装 (1) 安装Composer (2) 下载最新Larvel框架 https://g ...