# LDP中应用统一认证权限配置及开发说明

## 一、认证模块简介

LDP框架中的统一认证是通过UAA服务来实现的，UAA服务中主要用于认证服务、认证实例、领域应用组、认证应用相关模块。

#### 1.1 认证服务

​	认证服务包含认证方式、所属平台，服务名（内部服务），IP地址或者域名（第三方服务）、端口、登录接口，是否为内部服务等等服务相关信息。

#### 1.2 认证实例

​	认证实例包含关联的认证服、请求方式、请求头类型、返回类型、加密方式、请求参数模板、认证方法体、测试用户名和测试密码等相关认证信息。

#### 1.3 领域应用组

​	领域应用组作为应用管理组，可以有多个应用，但是一个领域只能关联一个认证实例，这个领域中所有的应用都通过这个实例来进行登录，也就是在同一个领域下的不同应用，可以通过realmCode相互认证，不用重复登录。新建认证领域的**说明**会作为登录界面的标题。

#### 1.4 认证应用

认证应用主要包含应用ID（最后会配置到网关中的clientId）、应用类型、以及属于哪一个认证领域。

## 二、认证配置流程

#### 2.1 新建认证服务

打开菜单**应用认证管理**=>**认证服务**，点击**新增**按钮

![认证服务列表](../progress/47认证服务列表.png)

在新增界面填上相关信息，认证模式RestFul是指通过HTTP请求的方式来实现认证；platform指内部系统，服务地址如果是内部服务，则填写服务名即可，如果是外部地址则需要填写ip或者域名；内部服务不需要填写端口，外部服务根据服务提供方信息填写（80则可以不填）；请求协议可选http或https，最后填写上接口地址，点击提交即可按钮即可。（PS：这里的接口地址 sysid的值需要和认证域编码保持一致）

![新增认证服务](../progress/48新增认证服务.png)

#### 2.2 新建认证实例

打开菜单**应用认证管理**=>**实例列表**，点击**新增**按钮

![实例列表](../progress/49实例列表.png)

选择实例对应的认证服务；填入认证实例名称，选择请求方法（GET或POST），请求类型和返回类型都是可选的，如果请求类型不为application/json，则默认都是通过url传参，否则通过body传参；请求参数模板是预留的参数模板，userName作为登录名，passWord作为登录密码。

query参数请求模板

```js
userid=:userName&secret=:passWord
```

body参数请求模板（请求方法为POST 和 请求类型为application/json 才会使用body参数）

```javascript
{
    "userName":":userName",
     "password":":passWord"
}
```

认证方法体，则是作为登录是否通过的验证方式，由于登录接口的返回不确定，所以这里需要自行写出验证方法，验证方法例子：

```javascript
function validate(resultData, userName) {
    var resultData = JSON.parse(resultData);
    if (resultData.success){
        return true;
    }
    return false;
}
```

测试用户名、测试密码可以用来测试登陆是否能成功。

![新增实例](../progress/50新增实例.png)

#### 2.3 新建领域用户组

打开菜单**应用认证管理**=>**领域应用组**，点击**新增**按钮

![领域列表](../progress/51领域列表.png)

填上认证域编码（也就是realmCode），**认证域编码**非常重要，后续的认证多处都会用到它。填写认证域名称，选择应用类型（一般都是standard），选择认证实例（使用这个实例来对这个领域下的应用做登录），填写说明（PS：**这里的说明会作为登录界面的标题**），密码重置URL（**这里不填写，则登录框没有忘记密码的连接**），可以直接通过获取URL按钮直接赋值。

![新增领域](../progress/52新增领域.png)

#### 2.4 新建应用

打开菜单**应用认证管理**=>**新建应用**，点击**新增**按钮

![应用列表](../progress/53应用列表.png)

在新增应用时，会自动生成应用ID（后续需要填到网关中），填上认证应用名称，认证应用类型standard，选择输入哪个认证领域。

![新增应用](../progress/54新增应用.png)

## 三、LDP前端脚手架标准接入流程

UAA的接入分为三种，一种是通过LDP脚手架接入，一种是通过非LDP脚手架接入，一种通过开放接口接入。如果是非LDP脚手架接入，参考[四、其它前端脚手架接入流程](#四、其它前端脚手架接入流程)。如果是通过开发接口接入，参考[五、开放接口接入流程](#五、开放接口接入流程)。

#### 3.1 拷贝UAA资源库的前端资源

通过脚手架接入，需要在LDP部署服务器上的用户目录的UAA资源库目录中，如果是普通用户的话，资源目录在

```shell
/home/${username}/.ldp/uaaresources
```

如果是root账户，资源目录则在（下面的例子都用root账号举例）

```shell
/root/.ldp/uaaresources
```

在linux下，用户目录都可以用【~】符号来表示

UAA资源库中有需要拷贝两个目录的资源文件，一份是模板文件，另外一份是 模板文件的js、css、imge等静态文件。

1、切换到模板文件目录

```
cd ~/.ldp/uaaresources/uaatemplates/static/templates/default
```

执行拷贝命令，拷贝的目录名称为新增的**认证域编码**，例如，这里是example

```
cp -r mcs/ example/
```

2、切换到静态文件目录

```shell
cd ~/.ldp/uaaresources/uaatemplates/static/default
```

执行拷贝命令

```shell
cp -r mcs/ example/
```

这样前端资源的静态文件拷贝就完成了，如果有某个领域的登录界面需要修改，就可以到对应的认证域编码(realmCode)目录下修改模板文件。

#### 3.2 添加系统标识为领域的账号

打开菜单 **系统维护** => **账号管理** ，点击 **新增** 按钮

![用户列表](../progress/60账号列表.png)

在新增界面，输入相关的账号信息，然后选择 **系统标识** 为对应的认证域。后续此账号可以登录此认证域下所有的应用

![新增账号](../progress/61指定领域用户.png)



#### 3.3 给新增的应用添加菜单

打开菜单 **系统维护** => **应用菜单维护** ，选中刚才 **新建的应用**

![新增应用菜单](../progress/55新增应用菜单.png)

鼠标悬浮在根节点，点击 **新增** 按钮，填入相应的菜单数据，点击提交即可。（PS：**选择菜单类型的时候需要注意，导航目录下可以是导航目录或功能菜单，但是功能菜单下只能是功能视图页面，功能视图页面下不能挂载任何菜单**）。

![新增子菜单](../progress/56新增子菜单.png)

新增菜单后，需要新建角色并对新菜单进行授权。

#### 3.4 新增新应用角色并对菜单进行授权

打开菜单 **权限管理** => **角色管理** ，点击 **新增** 按钮

![角色列表](../progress/57角色列表.png)

选择角色类型为 **应用** ，选择对应的应用，输入角色名称和备注，点击 **提交** 即可

![新增应用角色](../progress/58新增应用角色.png)

点击 **应用菜单权限** ，选择对应的应用和角色，选择需要关联的菜单，点击保存即可。

![关联菜单](../progress/59新应用角色关联菜单.png)

点击，角色关联用户，选择对应的用户

![关联用户](../progress/62关联用户.png)

#### 3.5 修改网关配置文件

打开nacos，在网关的配置文件gateway-service-naocs.yml种添加上应用配置 id、realmcode、clientId。**其中id为自定义，realmcode是认证域编码，clientId是应用ID**。

![网关配置](../progress/63gateway配置.png)

#### 3.6 修改前端脚手架工程

通过git clone 前端脚手架工程

```shell
git clone http://gitlab.dev.shxrtech.com/ldp/ldp-appui-example.git -b develop
```

根据网关配置，修改脚手架配置文件 **.env.development** （开发用）， **.env.production** （发布用）。

![脚手架配置](../progress/64脚手架配置.png)

并且修改 **VUE_APP_PROXY_SERVER** 为对应的网关地址。

#### 3.7 启动前端工程

执行以下命令，启动脚手架工程

```shell
#执行安装
npm install
#运行脚手架工程
npm run dev
```

启动好后，会自动跳转到登录界面，图中框住的部分，是新建认证域时的说明字段。

![登录界面](../progress/65登录.png)

输入创建的用户信息，点击登录

![登录成功](../progress/66登录成功.png)

## 四、其它前端脚手架接入流程

#### 4.1 非LDP脚手架接入

如果使用非LDP前端脚手架接入，同时也使用UAA的前端登录界面，可以直接跳转到UAA登录界面

http://{server}/uaa-service/viewlogin?realmcode={realmcode}&clientid={clientid}&redirect={redirect}

URL参数含义

| 参数          | 说明                   | 示例                                 |
| ------------- | ---------------------- | ------------------------------------ |
| **server**    | 服务地址               | sinra-server0:9080                   |
| **realmcode** | 认证域编码             | app                                  |
| **clientid**  | 应用ID                 | 1552294656514                        |
| **redirect**  | 登录成功后的重定向地址 | http://sinra-server0:9080/example/#/ |

按照示例跳转到以下地址：

http://sinra-server0:9080/uaa-service/viewlogin?realmcode=app&clientid=1552294656514&redirect=http%3A%2F%2Fsinra-server0%3A9080%2Fexample%2F%23%2F

登录成功后，会直接重定向到URL上的redirect地址，这里会重定向到：

http://sinra-server0:9080/example/#/?token={token}&appid={clientid}

这里需要实现截取回调地址上的token和clientId，并在之后的请求的请求头上携带上token的值。

```javascript
//在路由守卫中获取token，伪代码代码示例参考：
router.beforeEach(async(to, from, next) = >{
    //获取用户token
    constuserToken = to.query['token']
    if (!userToken) {
        //不存在token,跳转uaa登录地址
        window.top.location.href = uaa认证接口地址
    } else {
        //存在token通过接口获取用户信息
        axios.get('mcs-service/user/userinfo', {
            headers: {
                //token
                X-Ldp-Token: userToken,
                //应用领域编码
                X-Realm: realmcode
            }
        }).then(res = >{
            console.log('用户信息:' + res.data)
        }).
        catch(error = >{
            console.log('请求失败')
        })
    })
```



## 五、开放接口接入流程

#### 5.1 通过开放接口获取token

如果应用想使用自己的登录界面，可以在MCS创建完服务、实例、领域、应用后，通过开放接口的方式获取到token。

开放接口地址: **http://{server}/uaa-service/open/login**

参数分别为：

| 参数      | 说明       |
| --------- | ---------- |
| uname     | 用户名     |
| upass     | 用户密码   |
| clientid  | 应用ID     |
| realmcode | 认证域编码 |

接口信息详情请参考：http://api.dev.shxrtech.com/project/65/interface/api/2314

获取到请求返回的token之后，需要在之后的请求头中携带token

```javascript
headers: {
    //token
    X-Ldp-Token: userToken,
    //应用领域编码
    X-Realm: realmcode
}
```
#### 5.2 通过开放接口用户名免密登录获取token
使用此接口前提条件是需要将请求方ip 地址配置在uaa 免密登录白名单中,可在MCS中配置，如下：

![白名单配置](../imgs/system-whitelist.png)


开放接口地址: **http://{server}/uaa-service/open/user/token**  

request method: **POST**

request Url 参数分别为：

| 参数      | 说明       |
| --------- | ---------- |
| userName  | 用户名     |
| clientId  | 应用ID     |
| realmCode | 认证域编码  |

返回结果，样例：

```javascript 
{"code":200,
  "message":"success",
  "data":
  {
    "userid":"admin","username":"","email":"","orgid":"",
    "token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJjbGllbnR0eXBlIjoicmVzdCIsImF1ZCI6Im1jcyIsInN1YiI6ImFkbWluIiwiY2xpZW50aWQiOiIxNTUyMjk0NjU2NTE1IiwiaXNzIjoidWFhIiwicmVhbG1jb2RlIjoibWNzIiwiZXhwIjoxNTk3OTk2MjkzLCJ1c2VyaWQiOiJhZG1pbiIsImlhdCI6MTU5Nzk4MTg5MywianRpIjoiNWJkMjQwZTItMzEwNy00NzgwLWIwMjItMDkwZTZjZjdmYWU4In0.F3F1Ye0F2EsRiS2p_PQx4TONKnl61JvOpGEhvbzmXpQMRmES7-19NMUwdXUvjRfkq-vE4_HH-5Ni4jRQ6wj45if-LqB2XMmLDeE3msUDb_JLkQTLv_7zk8-ccIZio1Z-1A3ZwUp89S3FjCTgW7zcq4uN8YqRDFHdGxkoIA3IMMo",
    "realmCode":"mcs","clientId":"1552294656515"
   }
    
}
```
#### 5.3 外部应用通过WEB URL带token方式登录LDP应用
通过5.2中方法，获取到数据，拼接跳转URL如下格式：

**http://{ldp-web-server}?token={token}&appid={clientId}**

参数说明：

 参数      | 说明       |
| --------- | ---------- |
| ldp-web-server  | 应用web地址     |
| token  | 用户登录凭证    |
| clientId | 应用id  |
