历史版本10 :前台单点登录之AES加密示例 返回文档
编辑时间: 内容长度:图片数:目录数: 修改原因:

目录:

1. 概述编辑

1.1 版本

报表服务器版本插件版本
10.0V1.0

1.2 问题描述

前台单点登录 时,用户在自定义登录页面内输入的用户信息需要发送到数据决策系统中进行登录验证。

在发送过程中,以明文形式传输的用户密码易受到劫持攻击。如何保障用户密码传输的安全性呢?

1.3 实现思路

FineReport 支持使用 AES 对称加密方式对用户输入的密码进行加密,将加密后的密文发送至数据决策系统。

系统会在后台进行解密,当解密后的密码与数据决策系统内用户密码一致时,用户即可成功登录数据决策系统。

1.3.1 AES对称加密逻辑

用户在自定义登录页面内输入的明文密码会经过以下步骤从前台传输至后台,最终用于登录验证。如下图所示:


发送方:

  • 用户在网页中输入密码,即明文 P

  • 使用工程特有的密钥 K 对明文密码 P 进行 AES 加密,得到密文 C

  • 调用 前台单点登录接口 传输密文 C,并加上 encrypted 参数说明密码是经过 AES 加密后的密文

接收方:

密文 C 经过网络传输到达接收方,系统会用自带的 AES 解密函数和秘钥 K 去解密密码。

1.3.2 实现步骤

注1:推荐方案一,通过插件能够实时的获取秘钥,不用担心秘钥改变的情况。

注2:工程初始化秘钥为 16 位字符串,保存在 FineDB 数据库的 SecurityConfig.frontSeed 字段中,用户可以使用初始化密钥或者自定义修改密钥。详情请参见:fine_conf_entity可视化配置

方案步骤
方案一:通过插件获取密钥

1)安装 frontseed 插件

2)新建登录页面,在HTML文件中实现以下步骤:

  • 通过/url/frontseed请求返回值中的 frontSeed 字段获取密钥

  • 使用 BI.aesEncrypt(password, frontSeed) 对明文进行 AES 加密

  • 设置是否使用加密的参数 encrypted 为 true,告知后台该密码是经 AES 加密后的密文,需进行解密

  • 调用单点登录接口,将密文传输到后台

  • 系统后台进行解密和登录验证

3)将 HTML 文件放到指定位置

方案二:通过数据库查询获取密钥

1)连接 FineDB,通过 SQL 查询获取密钥

2)新建登录页面,在HTML文件中实现以下步骤:

  • 使用 BI.aesEncrypt(password, 密钥) 对明文进行 AES 加密

  • 设置是否使用加密的参数 encrypted 为 true,告知后台该密码是经 AES 加密后的密文,需进行解密

  • 调用单点登录接口,将密文传输到后台

  • 系统后台进行解密和登录验证

3)将 HTML 文件放到指定位置

2. 方案一:通过插件获取密钥编辑

注:该方案需要关闭 安全防护 中的「内容嗅探攻击防护」功能。

2.1 安装插件

注:请勿解压下载得到的压缩包,直接安装即可。

点击下载插件并手动安装:fr-plugin-frontseed-1.0.zip

设计器插件安装方法参照:设计器插件管理

服务器安装插件方法参照:服务器插件管理

2.2 新建登录页面

新建 HTML 文件自定义决策平台登录页面,命名为Aeslogin.html。代码如下所示:

注:根据实际情况修改代码中的访问路径或端口号。

点击下载并解压获得 HTML 文件:Aeslogin1.zip

<html><head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>跨域登录加密Demo</title>
    <!-- 自定义样式,根据实际需求使用 -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <!-- aes加密需要使用到对应的方法,因此需要引用工程中的fineui。如果自行实现aes加密可以不引用 -->
    <script src="http://localhost:8075/webroot/decision/file?path=/com/fr/web/ui/fineui.min.js&type=plain&parser=plain"></script>
    <style>
        .container {
            display: flex;
            justify-content: center;
        }
        .login-box {
            width: 300px;
            margin-top: 100px;
        }
        .login-box h2 {
            font-size: 26px;
            text-align: center;
            margin-bottom: 25px;
        }
        .login-item {
            margin-bottom: 20px;
        }
    </style>
<body>
<div class="container">
    <form class="login-box" action="" method="post" onsubmit="return false;">
        <h2>数据决策系统</h2>
        <div class="login-item">
            <label for="inputUsername" class="sr-only">用户名</label>
            <input type="text" id="inputUsername" class="form-control" placeholder="用户名" required="" autofocus="">
        </div>
        <div class="login-item">
            <label for="inputPassword" class="sr-only">密码</label>
            <!--  autocomplete="off"属性,阻止浏览器从cookies获取数据填充登录表单 -->
            <input type="text" id="inputPassword" οnfοcus="this.type='password'" class="form-control" placeholder="密码" required="" autocomplete="off">
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit" id="submitBtn">登录</button>
    </form>
</div>
<script>
    document.getElementById("submitBtn").addEventListener("click", function () {
        doSubmit();
    });
    function doSubmit() {
        var username = document.getElementById("inputUsername").value.trim();   //获取输入的用户名
        var password = document.getElementById("inputPassword").value.trim();   //获取输入的密码
        if (username === "") {
            window.alert("请输入用户名");
            return false;
        }
        if (password === "") {
            window.alert("请输入密码");
            return false;
        }
        $.ajax({
            url: "http://localhost:8075/webroot/decision/url/frontseed",    //获取AES密钥
            dataType:"jsonp",
            jsonp:"callback",
            success: function (res1) {
                var url = "http://localhost:8075/webroot/decision/login/cross/domain"
                    + "?fine_username=" + encodeURIComponent(username) + "&fine_password=" + encodeURIComponent(BI.aesEncrypt(password, res1.frontSeed))
                    + "&encrypted=true&validity=-1";    //调用前台单点登录接口
                $.ajax({
                    url: url,//单点登录的管理平台报表服务器
                    // timeout: 5000,//超时时间(单位:毫秒)
                    dataType:"jsonp",//跨域采用jsonp方式
                    jsonp:"callback",
                    success: function (res) {
                        if (res.url) {
                            window.location = res.url;  //跳转到数据决策系统
                        } else {
                            alert(res.errorMsg);
                        }
                    },
                    error: function () {
                        console.log(arguments)
                        // alert("超时或服务器其他错误");// 登录失败(超时或服务器其他错误)
                    }
                });
            },
            error: function () {
                console.log(arguments)
            }
        });
    }
</script>
</body>
</html>

2.3 将 HTML 文件放到指定位置

将 HTML 文件保存至%FR_HOME%/webapps/webroot文件夹下,如下图所示:

2.4 效果查看

在浏览器中访问链接:http://localhost:8075/webroot/Aeslogin.html,输入数据决策系统内用户的账号和密码。

以用户 Anna(Anna,123456)为例,输入登录信息后点击「登录」按钮,即可成功访问数据决策系统。如下图所示:

用户点击「登录」按钮后,前台会对用户输入的登录信息进行以下处理:

1)对输入的密码进行 AES 加密并使用encodeURIComponent()对密文中的特殊字符进行转码。将最终得到的密文作为接口入参。

    示例:输入密码为「123456」,工程密钥为「1b1cd997b655aa3」。

    使用密钥对输入密码进行 AES 加密,得到密文「MrIg7ZKP5cEIliWaWseZiA==」,再进行转码,到密文「MrIg7ZKP5cEIliWaWseZiA%3D%3D」。

2)设置 encrypted 参数为 true,告知后台该密码是经 AES 加密后的密文,需进行解密。

3)调用前台单点登录接口,即可成功访问数据决策系统。

注:用户可在提供的代码中自行添加alert(url);来获取页面 URL,并通过 URL 检查接口的参数是否正确。

3. 方案二:通过数据库查询获取密钥编辑

3.1 获取密钥

1)连接内置数据库 FineDB,详情请参见:FineDB 数据库简介 中 2.3 节设计器连接 FineDB。

2)新建数据库查询,选择数据连接为 FineDB。

输入 SQL 语句:select value from FINE_CONF_ENTITY where id = 'SecurityConfig.frontSeed',点击「预览」,得到的「value」值即为工程初始化密钥。如下图所示:

3.2 新建登录页面

新建 HTML 文件自定义决策平台登录页面,命名为Aeslogin.html。代码如下所示:

注1:需要将本文提供的代码进行以下修改:

    将 encodeURIComponent(BI.aesEncrypt(password, "1b1cd997b655aa33")) 中的示例密钥 1b1cd997b655aa33 更改为 3.1 节所获取的自身工程密钥。

    示例:若自身工程密钥为 dab840f81ed457d2 ,则更改为 encodeURIComponent(BI.aesEncrypt(password, "dab840f81ed457d2"))

注2:根据实际情况修改代码中的访问路径或端口号。

点击下载并解压获得 HTML 文件:Aeslogin2.zip

<html><head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>跨域登录加密Demo</title>
    <!-- 自定义样式,根据实际需求使用 -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <!-- aes加密需要使用到对应的方法,因此需要引用工程中的fineui。如果自行实现aes加密可以不引用 -->
    <script src="http://localhost:8075/webroot/decision/file?path=/com/fr/web/ui/fineui.min.js&type=plain&parser=plain"></script>
    <style>
        .container {
            display: flex;
            justify-content: center;
        }
        .login-box {
            width: 300px;
            margin-top: 100px;
        }
        .login-box h2 {
            font-size: 26px;
            text-align: center;
            margin-bottom: 25px;
        }
        .login-item {
            margin-bottom: 20px;
        }
    </style>
<body>
<div class="container">
    <form class="login-box" action="" method="post" onsubmit="return false;">
        <h2>数据决策系统</h2>
        <div class="login-item">
            <label for="inputUsername" class="sr-only">用户名</label>
            <input type="text" id="inputUsername" class="form-control" placeholder="用户名" required="" autofocus="">
        </div>
        <div class="login-item">
            <label for="inputPassword" class="sr-only">密码</label>
            <!--  autocomplete="off"属性,阻止浏览器从cookies获取数据填充登录表单 -->
            <input type="text" id="inputPassword" οnfοcus="this.type='password'" class="form-control" placeholder="密码" required="" autocomplete="off">
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit" id="submitBtn">登录</button>
    </form>
</div>
<script>
    document.getElementById("submitBtn").addEventListener("click", function () {
        doSubmit();
    });
    function doSubmit() {
        var username = document.getElementById("inputUsername").value.trim();
        var password = document.getElementById("inputPassword").value.trim();
        if (username === "") {
            window.alert("请输入用户名");
            return false;
        }
        if (password === "") {
            window.alert("请输入密码");
            return false;
        }
        var url = "http://localhost:8075/webroot/decision/login/cross/domain"
                    + "?fine_username=" + encodeURIComponent(username) + "&fine_password=" + encodeURIComponent(BI.aesEncrypt(password, "dab840f81ed457d2"))
                    + "&encrypted=true&validity=-1";
        $.ajax({
                url: url,//单点登录的管理平台报表服务器
                timeout: 5000,//超时时间(单位:毫秒)
                dataType:"jsonp",//跨域采用jsonp方式
                jsonp:"callback",
                success: function (res) {
                    console.log(res);
                    if (res.errorCode) {
                        window.alert(res.errorMsg);
                    }else {
                        // 保存token并跳转到对应链接
                        window.location.href = "http://localhost:8075/webroot/decision";
                    }
                },
                error: function () {
                    alert("超时或服务器其他错误");// 登录失败(超时或服务器其他错误)
                }
        });
    }
</script>
</body>
</html>

3.3 将 HTML 文件放到指定位置

将 HTML 文件保存至%FR_HOME%/webapps/webroot文件夹下,如下图所示:

3.4 效果查看

在浏览器中访问链接:http://localhost:8075/webroot/Aeslogin.html,输入数据决策系统内用户的账号和密码。

以用户 Anna(Anna,123456)为例,输入登录信息后点击「登录」按钮,即可成功访问数据决策系统。如下图所示:


用户点击「登录」按钮后,前台会对用户输入的登录信息进行以下处理:

1)对输入的密码进行 AES 加密并使用encodeURIComponent()对密文中的特殊字符进行转码。将最终得到的密文作为接口入参。

    示例:输入密码为「123456」,工程密钥为「1b1cd997b655aa3」。

    使用密钥对输入密码进行 AES 加密,得到密文「MrIg7ZKP5cEIliWaWseZiA==」,再进行转码,到密文「MrIg7ZKP5cEIliWaWseZiA%3D%3D」。

2)设置 encrypted 参数为 true,告知后台该密码是经 AES 加密后的密文,需进行解密。

3)调用前台单点登录接口,即可成功访问数据决策系统。

注:用户可在提供的代码中自行添加alert(url);来获取页面 URL,并通过 URL 检查接口的参数是否正确。