Web服务统一身份认证协议设计与实现

news2024/12/27 10:10:55

单点登录(SSO)是目前比较流行的企业业务整合的解决方案之一,它的机制是在企业网络用户访问企业网站时作一次身份认证,随后就可以对所有被授权的网络资源进行无缝的访问,而不需要多次输入自己的认证信息.Web服务具有松散耦合、语言中立、平台无关性、开放性的特性,通过对集中单点登录模型的分析和比较,提出了基于Web服务和token-id的单点登录管理框架.介绍了该系统的体系机构及关键部分的工作原理.

本项目主要是采用spring boot +mysql 实现。重点描述如何重代码结构上实现 Web服务统一身份认证协议设计与实现功能。

主要功能如下

  1. token生成与管理
  2. 系统接入token 实现sso 单点登录
  3. 统一身份token 认证与校验
  4. 分布式系统接入sso模式

项目整体结构

在这里插入图片描述

一:sso 管理

原理,利用http 头部信息保存token 匹配服务器的session信息,进行校验。当用户首次登录系统,注册sso 信息

(1):注册sso token

    public ResultPo register(String sessionId, String token, long uid) {
        String session = getSession(uid, token);
        if (session != null && session.equals(sessionId)) {
            ResultPo result = ResultPo.create(200, "sso token exist");
            SSoPo po = redisService.getPo(SSoPo.class, new MongoPrimaryKey("session_id", session));
            if (po != null) {
                po.setMtime(TimeUtils.Now());
                redisService.updatePo(po);
                result.appendData("sso", po);
                return result;
            }
        } else if (session != null) {
            this.removeSession(session, token);
        }

        SSoPo sso = new SSoPo();
        sso.setUid(uid);
        sso.setSessionId(sessionId);
        sso.setToken(token);
        sso.setMtime(TimeUtils.Now());
        SSoPo po = redisService.getPo(SSoPo.class, sso.getPrimaryKeys());
        if (po != null) {
            po.setMtime(TimeUtils.Now());
            redisService.updatePo(po);
            ResultPo result = ResultPo.create(200, "sso token exist");
            result.appendData("sso", sso);
            return result;
        }
        redisService.savePo(sso);
        ResultPo result = ResultPo.create(200, "sso register successful");
        result.appendData("sso", sso);
        redisService.hsetString(getSessionPool(token), uid + "", sessionId, sso.getClass().getAnnotation(Cache.class).timestamp());
        return result;
    }

    /**
     * token 生成器
     *
     * @return
     */
    public String generateTokeCode() {
        String value = System.currentTimeMillis() + new Random().nextInt() + "";
        System.out.println(value);

        long currentTime = System.currentTimeMillis();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy年-MM月dd日-HH时mm分ss秒");
        Date date = new Date(currentTime);
        System.out.println(formatter.format(date));

        //获取数据指纹,指纹是唯一的
        try {
            MessageDigest md = MessageDigest.getInstance("md5");
            byte[] b = md.digest(value.getBytes());//产生数据的指纹
            //Base64编码
            BASE64Encoder be = new BASE64Encoder();
            be.encode(b);
            System.out.println(be.encode(b));
            return be.encode(b);//制定一个编码
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }


把信息保存到中心服务中,把用户uid sessionid token 三者关系建立起来。

(2):校验sso

 public ResultPo validateSession(String sessionId, String token) {
        SSoPo sso = redisService.getPo(SSoPo.class, new MongoPrimaryKey("session_id", sessionId));
        if (sso == null) {
            return ResultPo.create(ErrorCode.SESSION_ERROR, "session not found");
        }
        //过期
        boolean expr = TimeUtils.Now() > (sso.getMtime() + sso.getClass().getAnnotation(Cache.class).timestamp());
        if (expr) {
            removeSession(sso.getSessionId(), token);
            return ResultPo.create(ErrorCode.SESSION_ERROR, "session long old");
        }
        ResultPo po = ResultPo.create(200, "session validate successful");
        po.appendData("sso", sso);
        return po;
    }

用户在分布式系统中请求都携带此token 中,不用在此进行登录或者用户信息的校验,只需要通过token 和application所颁发的token 进行校验。

/**
     * 验证web 应用是否合法
     *
     * @param serverId
     * @param token
     * @return
     */
    public ResultPo validateApp(long serverId, String token) {
        HashMap<String, Object> params = new HashMap<>();
        params.put("serverId", serverId);
        params.put("token", token);
        String json = HttpUtil.getInstance(token).post("", this.url + "/sso/appValidate", params);
        return BaseUtils.getResultPo(json);
    }

(3):token 销毁

 @Override
    public ResultPo removeSession(String sessionId, String token) {
        SSoPo sso = redisService.getPo(SSoPo.class, new MongoPrimaryKey("session_id", sessionId));
        if (sso == null) {
            return ResultPo.create(200, "");
        }
        redisService.deletePo(SSoPo.class, new MongoPrimaryKey("session_id", sessionId));
        redisService.deleteHField(getSessionPool(token), sso.getUid() + "");
        return ResultPo.create(200, "successful");
    }

二:服务器节点接入

服务节点的介入,任何系统授信信息都需要通过中心服务器颁发的token 与appid 进行注册校验,提供application的注册信息。颁发给每个系统。

(1):系统节点接入

应用节点定义:

@Document(collection = "server_node")
@Cache(cache = true)
public class ServerNode implements IMongoPo {

    @Field("server_id")
    @PrimaryKey(name = "server_id")
    @Indexed(name = "server_id", unique = true)  //索引  name为索引名称,unique=true,唯一索引
    private int serverId;
    @Field("type")
    private String type;
    @Field("name")
    private String name;
    @Field("url")
    private String url;
    @Field("token")
    private String token;
    @Field("port")
    private int port;
    @Field("weight")
    private int weight;
    @Field("status")
    private int status;

应用接入:

 @Override
    public ResultPo register(ServerNode serverNode) {
        boolean successful = serverDao.add(serverNode);
        if (successful) {
            return ResultPo.create();
        }
        return ResultPo.create(500, "create app error");
    }

分系统接入
定义一个服务的主要信息

"sso": [
    {
      "id": 1,
      "url": "http://127.0.0.1",
      "port": 8524,
      "weight": 1,
      "token": "123456"
    }
  ],

应用校验

应用系统进行网络通讯的时候,首先会进行application校验。

  @RequestMapping("appValidate")
    public ResultPo appValidate(HttpServletRequest request, long serverId, String token) {
        ResultPo resultpo = serverService.get(serverId);
        Object obj = resultpo.get("data");
        if (obj == null) {
            resultpo.setCode(500);
            resultpo.setMessage("application validate error!");
            return resultpo;
        }
        ServerNode node = (ServerNode) obj;
        if (!node.getToken().equals(token)) {
            resultpo.setCode(500);
            resultpo.setMessage("application validate error! token not march");
            return resultpo;
        }
        resultpo.setCode(200);
        resultpo.setMessage("application validate successful");
        return resultpo;
    }

package com.graduation.online.proxy;

import com.graduation.online.base.BaseUtils;
import com.graduation.online.base.enums.ServerType;
import com.graduation.online.base.model.ResultPo;
import com.graduation.online.base.service.ServerNodeService;
import com.graduation.online.base.utils.HttpUtil;

import java.util.HashMap;

public class SSoAgentServer extends BaseAgent {

    public SSoAgentServer() {
        super(ServerNodeService.getInstance().getServerNode(ServerType.SSO));
    }

    public ResultPo register(String sessionId, long uid) {
        HashMap<String, Object> params = new HashMap<>();
        params.put("sessionId", sessionId);
        params.put("token", token);
        params.put("uid", uid);
        String json = HttpUtil.getInstance(token).post(sessionId, this.url + "/sso/register", params);
        return BaseUtils.getResultPo(json);
    }


    /**
     * 验证系统用户session 同一身份
     *
     * @param sessionId
     * @return
     */
    public ResultPo validateSession(String sessionId) {
        HashMap<String, Object> params = new HashMap<>();
        params.put("sessionId", sessionId);
        params.put("token", token);
        String json = HttpUtil.getInstance(token).post(sessionId, this.url + "/sso/validate", params);
        return BaseUtils.getResultPo(json);
    }

    /**
     * 验证web 应用是否合法
     *
     * @param serverId
     * @param token
     * @return
     */
    public ResultPo validateApp(long serverId, String token) {
        HashMap<String, Object> params = new HashMap<>();
        params.put("serverId", serverId);
        params.put("token", token);
        String json = HttpUtil.getInstance(token).post("", this.url + "/sso/appValidate", params);
        return BaseUtils.getResultPo(json);
    }

    public long getSSoUId(String sessionId) {
        HashMap<String, Object> params = new HashMap<>();
        params.put("sessionId", sessionId);
        params.put("token", token);
        String json = HttpUtil.getInstance(token).post(sessionId, this.url + "/sso/uid", params);
        ResultPo result = BaseUtils.getResultPo(json);
        if (result.get("uid") == null) {
            return -1;
        }
        return Long.parseLong(result.get("uid").toString());
    }

    public String getSession(long uid) {
        HashMap<String, Object> params = new HashMap<>();
        params.put("uid", uid);
        params.put("token", token);
        String json = HttpUtil.getInstance(token).post(null, this.url + "/sso/getSession", params);
        ResultPo result = BaseUtils.getResultPo(json);
        if (result.get("sessionId") == null) {
            return "";
        }
        return result.get("sessionId").toString();
    }

    public ResultPo removeSession(String sessionId) {
        HashMap<String, Object> params = new HashMap<>();
        params.put("sessionId", sessionId);
        params.put("token", token);
        String json = HttpUtil.getInstance(token).post(sessionId, this.url + "/sso/remove", params);
        return BaseUtils.getResultPo(json);
    }
}

分系统用户进行登录时候,只需要在登录授权一次,既可以在任何系统进行数据读取,

三:简单演示

在这里插入图片描述

当用户登录系统后
在这里插入图片描述

后台sso 系统接收到用户认证信息

用户授权后,进入到系统

系统为应用授权
在这里插入图片描述

总结:本系统采用web 的sso 统一身份认知,利用一个简单的,实现web 认证管理,加密认证,分布式系统应用授权认证。

代码地址:git@github.com:twjitm/graduation-online-server-code.git

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

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

相关文章

Qt扫盲-Qt 属性系统记录

Qt 属性系统记录一、概述二、属性声明三、通过元对象系统读写属性四、简单例子五、动态属性六、对一个类添加额外的属性一、概述 Qt 提供了一个复杂的属性系统&#xff0c;类似于一些编译器供应商提供的系统。然而&#xff0c;作为一个独立于编译器和平台的库&#xff0c;Qt并…

Java基础07——集合

Java基础07——集合一、集合和数组的对比二、ArrayList成员方法三、集合练习1. 添加数字并遍历2. 添加学生对象并遍历学生类测试类输出结果3. 添加用户对象并判断是否存在用户类测试类输出结果4. 添加手机对象并返回要求的数据&#xff08;返回多个数据&#xff09;手机类测试类…

【算法】Day06

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录1. BST二叉搜索树的后序遍历序列2. 二叉树中和为某一值的路径&#xff08;二&#xff09;[回溯法]3. 字符串的排列 [全排列问题]4. 最小的K个数 [topK问题]普通小孩也要热爱生活&#xff01; 1. BST二叉搜索树的后序…

IF:6+ 综合分析揭示了一种炎症性癌症相关的成纤维细胞亚型在预测膀胱癌患者的预后和免疫治疗反应方面具有重要意义...

桓峰基因的教程不但教您怎么使用&#xff0c;还会定期分析一些相关的文章&#xff0c;学会教程只是基础&#xff0c;但是如果把分析结果整合到文章里面才是目的&#xff0c;觉得我们这些教程还不错&#xff0c;并且您按照我们的教程分析出来不错的结果发了文章记得告知我们&…

Linux 中断子系统(七):注册中断

Linux 注册中断的 API request_irq():不使用中断线程化request_threaded_irq():使用中断线程化中断线程化 为什么需要将中断下半部处理线程化,原因如下: 中断具有最高优先级,有中断发生时,会抢占进程,导致实时任务不能及时处理。中断上下文总是可以抢占进程上下文,这…

【PyTorch】教程:学习基础知识-(3) Datasets-DataLoader

Dataset & DataLoader PyTorch 提供了两个数据处理的基本方法&#xff1a;torch.utils.data.DataLoader torch.utils.data.Dataset 允许使用预加载的数据集以及自己的数据。 Dataset 存储样本及其对应的标签&#xff0c; DataLoader 在 Dataset 基础上封装了一个可迭代的对…

Python文本颜色设置

Python文本颜色设置实现过程&#xff1a;书写格式&#xff1a;数值表示的参数含义&#xff1a;常见开头格式&#xff1a;实例&#xff1a;实现过程&#xff1a; 终端的字符颜色是用转义序列控制的&#xff0c;是文本模式下的系统显示功能&#xff0c;和具体的语言无关。 转义序…

Acwing4699. 如此编码

某次测验后&#xff0c;顿顿老师在黑板上留下了一串数字 23333 便飘然而去。 凝望着这个神秘数字&#xff0c;小 P 同学不禁陷入了沉思…… 已知某次测验包含 nn 道单项选择题&#xff0c;其中第 i 题&#xff08;1≤i≤n&#xff09;有 ai 个选项&#xff0c;正确选项为 bi&…

CAS And Atomic

CAS(Compare And Swap 比较并交换)&#xff0c;通常指的是这样一种原子操作&#xff1a;针对一个变量&#xff0c;首先比较它的内存值与某个期望值是否相同&#xff0c;如果相同&#xff0c;就给它赋一个新值,底层是能保证cas是原子性的CAS的应用 在Java 中&#xff0c;CAS 操作…

Android开发-AS学习(三)(布局)

相关文章链接&#xff1a;Android开发-AS学习&#xff08;一&#xff09;&#xff08;控件&#xff09;Android开发-AS学习&#xff08;二&#xff09;(控件&#xff09;Android开发应用案例——简易计算器&#xff08;附完整源码&#xff09;二、布局2.1 Linearyout常见属性说…

测试NGINX和uwsgi.ini设置

1.uwsgi修改测试 将服务器升级到16核16G配置后&#xff0c;我将uwsgi.ini中的部分参数调整如下&#xff1a; processes 32 threads 16 结果是导致内存暴满&#xff0c;然后直接服务器都无法连接&#xff0c;导致服务器卡死。之前有博客说processes处理器*2&#xff0c;结果…

【阶段三】Python机器学习26篇:机器学习项目实战:LightGBM回归模型

本篇的思维导图: 项目实战(LightGBM回归模型) 项目背景 为促进产品的销售,厂商经常会通过多个渠道投放广告。本案例将根据某公司在电视、广播和报纸上的广告投放数据预测广告收益,作为公司制定广告策略的重要参考依据。 本项目应用LightGBM回归算法进行项目实战,整…

Nginx入门

介绍&#xff1a; 下载和安装&#xff1a; 安装过程&#xff1a; 1、因为nginx是由c语言编写的&#xff0c;所以需要下载gcc进行编译 yum -y install gcc pcre-devel zlib-devel openssl openssl-devel 2、下载nginx安装包 wget https://nginx.org/download/nginx-1.16.1.ta…

【Java基础知识 1】第一个Java程序(Java的第一步)

本文已收录专栏 &#x1f332;《Java进阶之路》&#x1f332; 编写一个Java程序 第一个Java程序非常简单&#xff0c;代码如下&#xff1a; public class Java01_HelloWorld {public static void main(String[] agrs){System.out.println("欢迎来到Java进阶之路&#x…

蓝桥杯 stm32 按键点灯 CubeMX

注&#xff1a;我们使用的是 HAL 库 文章目录前言一、按键 原理图&#xff1a;二、按键CubeMX配置:三、代码讲解1. 读按键&#xff1a;&#xff08; 三行代码&#xff09;2.按键消抖&#xff1a;3&#xff0c;按键点灯&#xff1a;总结实验效果&#xff1a;前言 一、按键 原理…

基于yolov5-v7.0开发构建银行卡号实例分割检测识别分析系统

在之前的文章中我们已经做了很多基于yolov5完成实例分割的项目&#xff0c;感兴趣的话可以自行移步阅读&#xff1a;《基于YOLOv5-v7.0的药片污染、缺损裂痕实例分割检测识别分析系统》《基于yolov5-v7.0开发构建裸土实例分割检测识别模型》《基于yolov5-v7.0开发实践实例分割模…

CSDN第24期周赛(记录一下)

▶ 爱要坦荡荡 (163.com) 每一道题都有思路&#xff0c;可是只有一道Accepted100%&#xff0c;一道很简单的50%&#xff0c;一道输出错误10%并报错Segmentation Fault&#xff0c;一道字符串有思路但是没时间了 一&#xff0c;蛇皮矩阵 Accepted 10% 报错Segmentation Fault…

2、矩阵介绍

目录 一、矩阵的构造 二、矩阵大小及结构的改变 三、矩阵下标的引用 1.矩阵下标访问单个矩阵元 2.线性引用矩阵元 3.访问多个矩阵元素 四、矩阵信息的提取 1.矩阵结构 2.矩阵大小 3.矩阵的数据类型 一、矩阵的构造 矩阵的构建方式有两种&#xff0c;一种与单元数组相…

【寒假每日一题】DAY.7 有序序列判断

牛客网例题&#xff1a;点我做题 【❤️温馨提示】先做题&#xff0c;再看讲解效果更佳哟 描述 输入一个整数序列&#xff0c;判断是否是有序序列&#xff0c;有序&#xff0c;指序列中的整数从小到大排序 或者从大到小排序(相同元素也视为有序)。输入描述&#xff1a; 第一行…

【C++】stack、queue的模拟实现及deque介绍

一、stack 1. 以vector作为底层容器 从栈的接口中可以看出&#xff0c;栈实际是一种特殊的vector&#xff0c;因此使用vector完全可以模拟实现stack。 由于stack的所有工作是底层容器完成的&#xff0c;而这种具有“修改某物接口&#xff0c;形成另一种风貌”的性质&#xf…