初识单点登录及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查看情况

原文地址:https://www.cnblogs.com/it-taosir/p/10021811.html

时间: 2024-11-10 05:51:44

初识单点登录及JWT实现的相关文章

认识单点登录

初识单点登录 最初接触到单点登录要追溯到3年多以前了,那时候看到的只是passport,当时要做全国所有社区的登录,然后就照着内部文档写了代码,然后就接入了(这里要提一句是百度与腾讯一旦形成产品的技术项目,文档都很不错)然后就没有然后了...... 而知识的珍贵程度却是这样的: 知识珍贵度排名: 听过 < demo过 < 实际工作用过 < 实际工作中被坑过< 实际工作中被多次坑过或者深入研究总结过 所以,脱离工作去学习node可能收效不大,脱离项目去说前端优化可能不让人信服,不被i

JWT(JSON Web Token) 多网站的单点登录,放弃session

多个网站之间的登录信息共享, 一种解决方案是基于cookie - session的登录认证方式,这种方式跨域比较复杂. 另一种替代方案是采用基于算法的认证方式, JWT(json web token)的方式. 参考链接: http://www.tuicool.com/articles/IRJnaa https://coderwall.com/p/8wrxfw/goodbye-php-sessions-hello-json-web-tokens 一.概念和定义 1.什么是jwt? Json web

看图理解JWT如何用于单点登录

单点登录是我比较喜欢的一个技术解决方案,一方面他能够提高产品使用的便利性,另一方面他分离了各个应用都需要的登录服务,对性能以及工作量都有好处.自从上次研究过JWT如何应用于会话管理,加之以前的项目中也一直在使用CAS这个比较流行的单点登录框架,所以就一直在琢磨如何能够把JWT跟单点登录结合起来一起使用,尽量能把两种技术的优势都集成到项目中来.本文介绍我从CAS思考得出的SSO的实现方案. 前言 其实CAS这个方案很好,非常强大,它最新的版本已经集成JWT了,所以要是不想自己开发单点登录的服务的话

spring+springmvc+Interceptor+jwt+redis实现sso单点登录

在分布式环境中,如何支持PC.APP(ios.android)等多端的会话共享,这也是所有公司都需要的解决方案,用传统的session方式来解决,我想已经out了,我们是否可以找一个通用的方案,比如用传统cas来实现多系统之间的sso单点登录或使用oauth的第三方登录方案? 今天给大家简单讲解一下使用spring拦截器Interceptor机制.jwt认证方式.redis分布式缓存实现sso单点登录 } publicvoid setUid(String uid) { this.uid = ui

springboot+security+JWT实现单点登录

本次整合实现的目标:1.SSO单点登录2.基于角色和spring security注解的权限控制. 整合过程如下: 1.使用maven构建项目,加入先关依赖,pom.xml如下: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20

基于JWT机制的单点登录

使用JWT实现单点登录时,需要注意token时效性.token是保存在客户端的令牌数据,如果永久有效,则有被劫持的可能.token在设计的时候,可以考虑一次性有效或一段时间内有效.如果设置有效时长,则需要考虑是否需要刷新token有效期问题(比如有效期是10分钟,10点登陆,有效时间到10点10分,10点09分再次请求一次,要刷新有效期,有效时间到10点19分). 使用JWT技术生成的token,客户端在保存的时候可以考虑cookie或localStorage.cookie保存方式,可以实现跨域

手把手教你学会 基于JWT的单点登录

??最近我们组要给负责的一个管理系统 A 集成另外一个系统 B,为了让用户使用更加便捷,避免多个系统重复登录,希望能够达到这样的效果--用户只需登录一次就能够在这两个系统中进行操作.很明显这就是单点登录(Single Sign-On)达到的效果,正好可以明目张胆的学一波单点登录知识. 本篇主要内容如下: SSO 介绍 SSO 的几种实现方式对比 基于 JWT 的 spring boot 单点登录实战 注意: ??SSO 这个概念已经出现很久很久了,目前各种平台都有非常成熟的实现,比如OpenSS

Spring Security整合JWT,实现单点登录,So Easy~!

前面整理过一篇 SpringBoot Security前后端分离,登录退出等返回json数据,也就是用Spring Security,基于SpringBoot2.1.4 RELEASE前后端分离的情况下,实现了登陆登出的功能,亮点就在于以JSON的形式接收返回参数.这个是针对单个后台服务的, 登录信息都存储在SecurityContextHolder缓存里.如果是两个或两个以上的应用呢,那该怎么办?Session是不能用了,Cookie自然也不能用,毕竟它俩是一对的. 曾想过用OAuth2来解决

单点登录(SSO)初识

单点登录英文全称Single Sign On,简称就是SSO.它的解释是:在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统. 普通的登录认证机制 cookie session 同域下(顶级域名相同,子域不同)的单点登陆(Cookie是不能跨域的,可以借助将Cookie的域设置为顶域,这样所有子域的系统都可以访问到顶域的Cookie的这种方式来实现) 单点登陆实现:CAS Server(可以实现不同域下的单点登录) 参考链接:单点登录(SSO)看这一篇就够了 原文地址:https