初识单点登录及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的构造器用于生成tokenJwtBuilder 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数据,就是其中保存的claimsreturn 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查看情况

 

转载于:https://www.cnblogs.com/it-taosir/p/10021811.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/248805.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue组件通信原理剖析(三)provide/inject原理分析

首先我们先从一个面试题入手。 面试官问&#xff1a; “Vue中组件通信的常用方式有哪些&#xff1f;” 我答&#xff1a; 1. props 2. 自定义事件 3. eventbus 4. vuex 5. 还有常见的边界情况$parent、$children、$root、$refs、provide/inject 6. 此外还有一些非props特性$att…

iMX6开发板-uboot-网络设置和测试

本文章基于迅为IMX6开发板 将iMX6开发板通过网线连接到路由器&#xff0c;同时连接好调试串口&#xff0c;上电立即按 enter&#xff0c;即可进入 uboot。然后输入命令 pri&#xff0c;查看开发板当前的配置&#xff0c;如下图所示可以看到 ip 地址、子网掩码 等信息。 本文档测…

Django ajax 检测用户名是否已被注册

添加一个 register.html 页面 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body> <form><p>用户名<input id"username" type&…

详解JDBC连接数据库

一、概念 1. 为了能让程序操作数据库&#xff0c;对数据库中的表进行操作&#xff0c;每一种数据库都会提供一套连接和操作该数据库的驱动&#xff0c;而且每种数据库的驱动都各不相同&#xff0c;例如mysql数据库使用mysql驱动&#xff0c;oracle数据库使用oracle驱动&#xf…

ZOJ4024 Peak

题意 给出一个数组 判断这个数组是否形成了一个“山峰” 即中间有个数最大 从第一个数到这个数递增 从这个数到最后一个数递减 模拟 从两端分别以递增和递减判断 看第一个不满足递增或递减的数是否相等并且没越界就可以了 AC代码&#xff1a; 1 #include<bits/stdc.h>2 u…

springmvc跨域问题

1、跨域问题&#xff1a; 按照网上所有的方法试了一遍&#xff0c;都没跨过去&#xff0c;正在无助之际&#xff0c;使用filter按照下面的方法解决的时候出现了转机&#xff1a; 添加filter&#xff1a; package com.thc.bpm.filter;import javax.servlet.*; import javax.serv…

成功秀了一波scala spark ML逻辑斯蒂回归

1、直接上官方代码&#xff0c;调整过的&#xff0c;方可使用 package com.test import org.apache.spark.{SparkConf, SparkContext} import org.apache.spark.mllib.classification.{LogisticRegressionModel, LogisticRegressionWithLBFGS} import org.apache.spark.mllib.e…

nodeJS中的异步编程

nodejs 不是单线程 在博客项目中关于异步问题&#xff1a; 1.当用户添加一条博客时 需要通过post方式向服务器发送数据 后台获取用户以post方式拿到传送过来的数据 然后存入数据库&#xff1a; 上面的代码&#xff1a;创建一个空字符串 当用户向服务器发送请求时出发data事件将…

nodeJs 操作数据库

首先在node中下载mysql包 npm install mysql 连接数据库 var mysql require(mysql); var con mysql.createConnection({host : localhost,user : root,password : root,database : blog });开启链接 con.connect();执行增删改查 不同功能创建不同的sql语句即可…

总结面试题——Javascript

文章目录1.闭包2.作用域链3.JavaScript的原型 原型链 有什么特点4.事件代理5.Javascript如何实现继承6.this对象7.事件模型8.new操作符9.ajax原理10.解决跨域问题11.模块化开发怎么做12.异步加载js的方式有哪些13.会造成内存泄漏的操作14.XML和JSON的区别15.webpack16.AMD和Com…

OAuth2.0 知多少

OAuth2.0 知多少 原文:OAuth2.0 知多少1. 引言 周末逛简书&#xff0c;看了一篇写的极好的文章&#xff0c;点击大红心点赞&#xff0c;就直接给我跳转到登录界面了&#xff0c;原来点赞是需要登录的。 可是没有我并没有简书账号&#xff0c;一直使用的QQ的集成登录。下面有一排…

五分钟带你摸透 Vue组件及组件通讯

一.组件化开发 组件 (Component) 是 Vue.js 强大的功能之一。组件可以扩展 HTML 元素&#xff0c;封装可重用的代 码。在较高层面上&#xff0c;组件是自定义元素&#xff0c;Vue.js 的编译器为它添加特殊功能。在vue中都是组件化开发的&#xff0c;组件化开发就是把一个完整的…

微信公众号开发-接入

一 首先实现内网穿透&#xff0c;公众号需要连接我们的服务器&#xff0c;内外无法访问&#xff0c;所以先实现自己的内网可以测试时连接外网&#xff0c;下载natapp&#xff0c;选择windows&#xff0c;顺便下载config,ini 配置文件。注册好购买免费的隧道 然后将token写入配置…

Vue 项目上线优化

上线项目的优化 优化上线项目&#xff0c;首先在上线打包时我们通过babel插件将console清除&#xff0c;当然对项目打包后的体积的影响是微乎其微&#xff0c;对项目的入口文件的改善也是很有必要的&#xff0c;因为在开发阶段和上线如果我们使用的是同一入口文件&#xff0c;…

Python并发编程—进程

多任务编程 1.意义&#xff1a; 充分利用计算机多核资源&#xff0c;提高程序的运行效率。 2.实现方案 &#xff1a;多进程 &#xff0c; 多线程 3.并行与并发 并发 &#xff1a; 同时处理多个任务&#xff0c;内核在任务间不断的切换达到好像多个任务被同时执行的效果&#xf…

Vue 脚手架中的.eslintrc.js代码规范 的解决

在我们使用Vue脚手架 创建项目时 尤其是团队共同开发项目时 会按照一个共同的代码规范来编程 创建Vue脚手架中有一个.eslintrc.js格式 但是在编程中我们通常会使用 shiftaltf 进行代码格式化 但是由于格式化后的代码 与Vue中的.eslintrc规范不协调 尤其是 “” &#xff1b; 以…

前端面试---Vue部分考点梳理

一. Vue的使用 1. Vue的基本使用 指令 插值 插值 表达式 指令 动态属性 v-html 会有XSS风险 会覆盖子组件 computed 和 watch computed 有缓存 data不变则不会重新计算watch 如何深度监听watch 监听引用类型时 拿不到oldVal v-for v-for 和 v-if 不能同时使用:key的值尽量…

.net core实现跨域

什么是跨域在前面已经讲解过了&#xff0c;这里便不再讲解&#xff0c;直接上代码。 一、后台API接口 用.net core创建一个Web API项目负责给前端界面提供数据。 二、前端界面 建立两个MVC项目&#xff0c;模拟不同的ip&#xff0c;在view里面添加按钮调用WEB API提供的接口进行…

TCP/IP简介

TCP/IP简介 OSI的“实现”&#xff1a;TCP/IP参考模型 并不完全符合OSI的七层参考模型&#xff0c;但我们可以理解为OSI的一种实现 TCP/IP协议简述 在很多情况下&#xff0c;它只是利用IP协议进行通信时&#xff0c;所必须用到的协议群的统称&#xff0c;具体来说&#xff0c;I…

Spring-Cloud 学习笔记-(4)负载均衡器Ribbon

目录 Spring-Cloud 学习笔记-&#xff08;4&#xff09;负载均衡器Ribbon1、前言2、什么是负载均衡2.1、问题分析2.2、什么是Ribbon3、快速入门3.1、实现方式一3.1.1、修改代码3.2、实现方式二3.2.1、启动类3.2.2、调用代码3.2.3、测试3.2.4、实现原理3.2.5、断点调式3.3、修改…