环球网校是美国纳斯达克上市企业欢聚时代(NASDAQ:YY)旗下品牌 | 住房和城乡建设部 建筑人才培训合作单位
您现在的位置在: > 公务员 > 招考资讯 >

什么是单点登录?如何进行登录和权限验证?

2023-10-21 来源:网络 作者:佚名

单点登陆原理及实现

随着业务发展,公司业务会不断壮大,每位业务就会存在用户登入和权限验证,不可能要求用户每位业务网站都登入一次,这个时侯,就须要单点登陆功能。下边将先介绍基本概念,之后以百度()为例进行讲解,最后用一个小反例讲解怎样实现(后台用node)。本文是假定读者对http合同等有基本了解。

#

哪些是单点登陆? #

单点登陆即用户在公司的某个网站登陆后,访问该公司相关的网站都是已登入状态,无需再进行登陆操作。比如我们登陆百度首页()后,访问百度晓得(),就早已登陆,无需再度登陆该站点,这就是单点登陆。这个功能很重要,以阿里巴巴来说,业务非常庞大,假如每位业务网站都要重新登陆,即便用户要动怒。

#

要实现单点登陆,就要验证该用户已登陆,问题就回归到用户的登入验证问题,怎么来确保该用户已登入。 #

用户登入和权限验证 #

先来看下服务是怎样进行登陆和权限验证的。我们登陆一个网页,进行操作,多个http恳求如下所示: #

整个登陆、权限验证、退出操作可以分为下边几个步骤:

#

1、用户输入用户名和密码,顾客端将用户名和加密后的密码发送至服务端。 #

2、服务端成功验证后,会生成一个加密的会话id,把装入id列表,同时,服务器会返回恳求,并在返回头中设置,让id储存在中。 #

3、用户发送其它恳求(恳求会携带中的id)至服务端如何实现单点登录,比如获取历史操作记录,查询顾客帐单等等。 #

4、服务端验证id的有效性,有效则返回正确信息。

#

5、用户发送退出登入操作。 #

6、服务端验证id,验证成功则从id列表中删掉该id,或把该id置为过期。

#

7、用户再度发出查询帐单恳求。

#

8、服务端验证id,发觉该id不存在或已过期,则提示用户重新登入。

#

从上述过程可以发觉,用户的权限验证,就是会话id的生成和校准,只要多个站点可以共享这个id,既可以完成单点登陆为何如此说呢?诱因如下: #

假定公司有两个站点,和,根据如今的构架设计,会把公共的业务模块具象下来,作为独立的服务。顾客信息作为一个公共功能,自然是独立的。构架设计图如下所示:

#

和有独立的服务器,但向顾客信息系统等公共模块是独立的,即顾客端恳求信息是,两个服务总统会向顾客信息管理系统进行顾客权限验证,通过验证才继续操作,返回顾客须要的信息。

#

之前我们早已的出推论,用户的权限验证,就是会话id的验证。假如两个站点的会话id共享,只需登入一次,两个站点的权限验证都可以完成。即单点登陆的关键在于两个站点怎样实现会话id()的共享,下边,我们以百度为例,剖析她们怎么实现共享的。 #

实例剖析

#

以百度为例,我们登陆百度首页()后,步入百度文库()、百度晓得()都是已登入状态,我们用调试工具分别看见几个站点的,发觉有部份是相同的。 #

实际上,为一级域名,其它都是二级域名,可以在二级域名间共享,只需对的进行设置即可。 #

前面这些情况比较可以共享,假如大家公司的各个业务站点一级域名一致,可以使用该方式实现单点登陆。并且,当站点之前不是同域,以及域名也不同时,是难以共享的。这是浏览器的同源策略导致的,目的是保护用户信息安全,避免不法分子盗取顾客信息,伪装用户进行操作。

#

我们以百度(),()两个站点为例进行测试,瞧瞧她们是怎样实现单点登陆的。实验步骤如下: #

1、清除两个站点的和登陆信息,使她们都处于非登入状态。

#

2、打开百度首页()并登陆,查看用户信息。

#

3、打开()站点,查看用户信息。 #

4、比对用户信息,发觉用户信息一致。即两者实现了单点登陆,只要登陆百度,即可登入网站。

#

我们查看两者,发觉二者中有多个值相同(比如都存在BDUSS这个值,且值相同),可以推测她们可能共用了一套用户信息系统,起码用户登入信息是有共享的。 #

在登陆是,我用(抓包工具,可自行百度)进行抓包,瞧瞧她们究竟进行了哪些操作?百度的登陆过程,抓包如下 #

通过剖析,我在图中用画了几道红线,开始第一个是百度帐号的登入验证,之后返回了一个加密的用户身分惟一标示(bdu),之后向站点发起恳求,该恳求携带用户身分标示(bdu),验证该标示后,返回会话id,并存在的的域中。如右图所示: #

如图所示,单点登陆过程可分为以下几个步骤:

#

1、用户李三打开网站,输入用户名密码,发送登陆恳求至服务器。 #

2、服务器向顾客信息管理中心发起恳求,验证李三用户名密码。 #

3、验证完后返回会话id和用户惟一标示(一串加密的字符串如何实现单点登录,用于跨域验证,暂时称它为sid)。

#

4、收到登陆成功信息和sid后,携带sid向服务器发起认证恳求。

#

5、服务器同样向顾客信息管理中心发起惟一标示认证。

#

6、客户信息管理中心认证成功返回会话id给服务器。

#

7、服务器返回会话id,会话id会储存在域的中。 #

8、后续访问网站,恳请还会携带会话id,后台领到id去认证。

#

这就是单点登陆的整个过程。 #

具体实现

#

下边用node模拟两个站点进行单点登陆模拟。 #

服务器1,有一个页面,两个插口。

#

登入页代码如下:

#


<html>
<head>
    <meta charset="utf-8" />
    <title>登录页面title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        div {
            height: 30px;
            width: 300px;
            margin: 20px auto; 
            text-align: center
        }
        div > label {
            display: inline-block;
            width: 80px;
            text-align: right;
            padding-right: 15px;
            box-sizing: border-box
        }
        div > input {
            width: 200px
        }
    style>
head>
<body onload="addEvent()">
    <div>
        <label>userName:label>
        <input type="text" id="user_name">
    div>
    <div>
        <label>passwordlabel>
        <input type="password" id="password">
    div>
    <div>
        <input type="button" id="submit" value="logon" style="width: 100px">
    div>
    <script>
        //添加按钮事件
        function addEvent() {
            let submit = document.getElementById('submit');
            if (submit) {
                submit.addEventListener('click', () => {
                    login();
                });
            }
          
        }
        //登录请求方法
        function login() {
            let userName = document.getElementById('user_name');
            let password = document.getElementById('password');
            $.ajax({
                url: '/login',
                method: 'GET',
                data: {
                    userName: userName.value,
                    password: password.value
                },
                success: function(result) {
                    //使用用户唯一标识进行跨域认证
                    crossdomain(result.id);
                    location.href = location.protocol + '//' + location.host + '/success';
                },
                error: function(error) {
                    console.error('登录错误');
                }
            })
        }
        //跨域用户认证请求
        function crossdomain(id) {
            let iframe = document.createElement('iframe');
            iframe.src = 'http://localhost:10000/crossdomain?id=' + id;
            iframe.style.display = 'none';
            document.body.appendChild(iframe);
        }
    script>
    <script src="./jquery.js">script>
body>
html>
 # 
#

服务端代码如下:

#

const express = require('express');
const app = express();
const cookieParser = require('cookie-parser');
app.use(cookieParser());
//未登录访问带接口返回“无权限”,登录后返回“站点3000登录成功”
app.get('/success', (req, res) => {
    let sessionId = req.cookies ? req.cookies.sessionId : '';
    if (sessionId && sessionId === '123456') {
        res.send('站点3000登录成功');
    } else {
        res.send('无权限');
    }
});
//登录接口,验证成功返回会话id和用户登录唯一标识
app.get('/login', (req, res) => {
    let name = req.query.userName;
    let password = req.query.password;
    if (name === 'user' && password === '123456') {
        res.cookie('sessionId', '123456', { expires: new Date(Date.now() + 10 * 1000)});
        res.send({
            id: '123456789'
        });
    } else {
        res.sendStatus(400);
    }
});
//静态资源访问路径,路由中包含/static
app.use('/static', express.static('./resources'));
app.listen(3000, () => console.log('Example app listening on port 3000!'))
 # 
#

服务器2只有两个插口,代码如下:

#

const express = require('express');
const app = express();
const cookieParser = require('cookie-parser');
app.use(cookieParser());
//对于已认证用户,返回‘站点10000登录成功’,反之,返回‘无权限’
app.get('/getcontent', (req, res) => {
    let sessionId = req.cookies ? req.cookies.sessionId : '';
    if (sessionId && sessionId === '123456') {
        res.send('站点10000登录成功');
    } else {
        res.send('无权限');
    }
});
//认证用户接口,根据用户唯一标识认证用户是否登录
app.use('/crossdomain', (req, res) => {
    let id = req.query.id;
    if (id === '123456789') {
        res.cookie('sessionId', '123456', { expires: new Date(Date.now() + 1000 * 1000)});
        res.send('有权限');
    } else {
        res.send('无权限');
    }
});
//静态资源访问路径,路由中包含/static
app.use('/static', express.static('./resources'));
app.listen(10000, () => console.log('Example app listening on port 10000!'))
 # 
#

责编:admin 返回顶部  打印

关于我们联系我们友情链接网站声明网站地图广告服务帮助中心