# 用户身份认证源使用说明
# 登录介绍
# 登录方式
登录方式,目前分为三种方式:主登录、辅助登录、直接跳转
主登录
主登录,是使用账号密码认证一种方式,其中密码至少由8位组成,传输过程也会进行加密。包括平台账号登录、LDAP登录等账号密码方式
辅助登录
辅助登录,是主登录的辅助方式,通常由第三方提供认证过程,点击登录会跳转认证页,比如OpenID方式
直接跳转
直接跳转登录,是一种快捷认证方式,在认证过程中,平台登录框不会出现,login请求会直接跳转认证页。比如微信扫码登录、数帆登录等
# 快速开始
# 使用平台账号登录
平台提供账号密码方式登录,且默认开启,如果应用需要快速集成用户身份认证,则可使用平台账号密码方式
# 使用社会化认证源登录
目前支持仅微信H5方式,QQ、支付宝、Sina、GitHub、百度、Google、Twitter、Facebook、Gitee、LinkedIn、Instagram
在支持计划中
此场景适用于网页公众号授权登录,具体开发文档详见以下地址
https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html#1
# 使用企业认证源登录
目前支持网易OpenID,钉钉、飞书、企业微信、WindowAD、AzureAD、SAML、OAuth2.0、CAS、LCAP、OIDC、WeLink
在支持计划中
需要申请OpenID合法站点信息,配置即可
# 使用私有化认证
如果上述方式不能满足认证需求,用户可以通过开发自定义认证插件完成认证流程,详见插件开发流程
# 登录配置开启
参数配置后,可以在账号认证和权限中心 -> 系统管理 -> 登录配置->平台登录页选项开启
主登录和辅助登录方式
直接跳转方式
# 插件认证开发
# 基本介绍
# 1.插件结构
认证插件,分为前端和后端两个部分
前端插件,负责浏览器参数传递,跳转等逻辑,支持JavaScript语言,产出物为js文件
后端插件,处理用户认证信息的获取流程,支持Java语言,产出物为jar文件
如果为主登录方式,前端js插件不需要开发
# 2. 使用对象
有语言基础的开发者,目前支持开发语言
- 服务端:Java
- 客户端:js
# 3. 环境要求
- 推荐jdk1.8及以上、maven
# 4. 插件开发流程
# 5. 插件工作原理
下图为插件交互流程,
如果主登录方式,过程1可以省去,用户输入账号密码后,提交给前端插件走后续流程,获取UserInfo
# 插件框架能力
# 参数配置
针对不同环境,认证过程需要配置的参数,在插件开发中,可以借助@ConfigKey注解,来完成注入
/**
* AppKey
*/
@ConfigKey(ConfigKeyContants.APP_KEY)
protected String appKey;
/**
* AppSecret
*/
@ConfigKey(ConfigKeyContants.APP_SECRET)
protected String appSecret;
2
3
4
5
6
7
8
9
10
11
声明的ConfigKey参数,可以在平台系统配置-登录配置中看到,如下图所示
# 异常处理
getAuthInfo 认证过程中,可以抛出以下异常,框架会自行处理,并在前端登录页提示给用户
异常名称 | 描述 |
---|---|
AuthException | 基础鉴权异常,鉴权相关的子异常应该继承此异常 |
AuthIncorrectCredentialsException | 认证异常,密钥错误 |
AuthUnknownAccountException | 认证异常,不存在此账户 |
AuthDisabledAccountException | 认证异常,账户禁用 |
# SDK
框架提供上下文能力PluginContext
- PropertiesContext 配置文件能力
- EncryptContext 加解密能力
- 常见加密工具类(Base64、MD5、AES等)
- RepositoryContext 持久化能力
- SerializeContext/DeserializeContext 序列化/反序列化能力
- 常见序列化工具类(json、xml)
- ClientContext 网络请求能力
- httpClient (okhttp、httpclient) 目前默认为OKhttp,后续扩展httpClient能力
# 退出登录
如果需要退出登录操作,则需要实现logout方法
/**
* 退出登录入口,子类可以实现
* @return
*/
default boolean logout(HttpServletRequest servletRequest, final Map<String, String> parameterMap){return true;}
2
3
4
5
# API
API列表如下
API | 描述 |
---|---|
IAuthService | 认证基类 |
AbstractPluginAuthService | 插件类型认证抽象类 (插件继承实现) |
AuthInfo | 认证信息 |
# IAuthService
# getAuthInfo
getAuthInfo为认证核心逻辑,开发者可以在实现与认证源的交互
HttpServletRequest servletRequest,
可以获取访问浏览器header、cookie等信息
Map<String, String> parameterMap
获取认证参数,其中参数名要保持一致
# logout
注销方法,如果认证源如果同步注销,则需要实现该方法
# getAuthPageUrl
获取认证页地址,如果认证源有落地认证页,则需要实现该方法,一般适用于OAuth协议场景
# AbstractPluginAuthService
默认注入了框架能力PluginConext,LoggerContext,插件类可直接使用
# doHandle
认证核心逻辑
# preHandle
认证前处理,可选择实现
# postHandle
认证后处理,可选择实现
# AuthInfo
字段 | 描述 | 是否必填 |
---|---|---|
userId | 唯一用户ID,类似OpenId等 | 是 |
userName | 登录系统使用的用户账号,类似手机号、邮箱等字段,UserName不可重复 | 是 |
displayName | 显示名称,一般为用户姓名、昵称等具备可读性的字段(例如微信昵称、QQ昵称、微博昵称等) | 否 |
authParam | 校验参数 (若参数不为空,则每次用户请求,则会再次发起校验) | 否 |
# 最佳实践
# 准备插件开发环境
# 安装JDK
根据本机环境下载对应安装包
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
下载安装好并配置环境变量JAVA_HOME,在命令行窗口执行Java命令
$ java -version
java version "1.8.0_341"
Java(TM) SE Runtime Environment (build 1.8.0_341-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.341-b10, mixed mode)
2
3
4
5
# 安装maven
下载安装maven包管理工具,并配置环境变量
https://maven.apache.org/download.cgi
$ mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: D:\workspace\tools\apache-maven-3.6.3\bin\..
Java version: 1.8.0_333, vendor: Oracle Corporation, runtime: C:\Program Files\Java\jdk1.8.0_333\jre
Default locale: zh_CN, platform encoding: GBK
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
2
3
4
5
6
# 安装开发工具
推荐安装Idea,下面案例以IntelliJ Idea为例
https://www.jetbrains.com/idea/download/#section=windows
# 创建插件工程
模板工程: auth-plugin-archetype https://lcpapp-static.nos-eastchina1.126.net/auth-plugin/devtools/simple-auth-plugin-archetype.zip)
可以基于demo工程(示例工程:https://lcpapp-static.nos-eastchina1.126.net/auth-plugin/devtools/sub-plugin.zip)来创建
下载模板工程代码,并解压至某个文件夹,如下图所示:
在根目录开启命令行窗口,执行以下命令,把模板工程安装到maven仓库
mvn install
创建插件应用,以Test插件为例(插件名称要求首字母大写,比如:Test1、Test2、Test3等),生成好的目录结构如下入所示
mvn archetype:generate
-DarchetypeGroupId=com.netease.cloud.nuims
-DarchetypeArtifactId=auth-plugin-archetype
-DarchetypeVersion=1.0.1
-DgroupId=com.netease.cloud.nuims
-DartifactId=test-plugin
2
3
4
5
6
依赖包处理
- nuims-auth-api-1.0.1.jar(https://lcpapp-static.nos-eastchina1.126.net/auth-plugin/devtools/nuims-auth-api-1.0.1.jar)
下载依赖包到本地,完成后对文件进行重命名, nuims-auth-api-1.0.1.jar
目前有两种方式可以依赖
方案一
mvn install 到本地仓库,pom.xml依赖
mvn install:install-file -Dfile=nuims-auth-api-1.0.1.jar -DgroupId=com.netease.cloud.nuims -DartifactId=nuims-auth-api -Dversion=1.0.1 -Dpackaging=jar -DgeneratePom=true
<dependency>
<groupId>com.netease.cloud.nuims</groupId>
<artifactId>nuims-auth-api</artifactId>
<version>1.0.1</version>
</dependency>
2
3
4
5
6
方案二:
本地依赖
新建libs目录,拷贝jar到libs目录下,进行pom.xml依赖
<dependency>
<groupId>com.netease.cloud.nuims</groupId>
<artifactId>nuims-auth-api</artifactId>
<version>1.0.1</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/nuims-auth-api-1.0.1.jar</systemPath>
</dependency>
2
3
4
5
6
7
应用目录介绍:
- 核心认证类
TestPluginAuthService.java
- 类声明文件
resources\META-INF\services\com.netease.cloud.nuims.auth.api.AbstractPluginAuthService
com.netease.cloud.nuims.plugin.extension.TestPluginAuthService
通用流程步骤为:
- 编写框架代码:
实现类TestPluginAuthService ,继承抽象类
AbstractPluginAuthService
,name
方法为认证标识 - 编码完成后声明: 在META-INF/services/com.netease.cloud.nuims.auth.api.AbstractPluginAuthService文件中声明
com.netease.cloud.nuims.plugin.extension.TestPluginAuthService
# 编写插件逻辑
核心方法doHandler,此方法可以完成与认证源的交互
@ConfigKey 为个性化参数声明,可以通过配置文件注入
以下为常见三种类型认证方式示例:
# 账号密码,适用于主登录方式
注意UserName、Password 为页面参数名称,不可自定义命名
public class Test1PluginAuthService extends AbstractPluginAuthService {
//账号为页面参数名称,不可更改
private String USERNAME_KEY = "UserName";
//密码为页面参数名称,不可更改
private String PASSWORD_KEY = "Password";
@Override
protected AuthInfo doHandle(HttpServletRequest servletRequest, Map<String, String> parameterMap) throws AuthException {
String userNameParams= parameterMap.get(USERNAME_KEY);
String passwordParams = parameterMap.get(PASSWORD_KEY);
if(Objects.nonNull(userNameParams) && Objects.nonNull(passwordParams)){
if("12345678".equalsIgnoreCase(userNameParams) && "12345678".equalsIgnoreCase(passwordParams)){
return AuthInfo.ofSuccess()
.setUserName(userNameParams)
.setDisplayName("昵称")
.setUserId(jsonObject.getString("userId"));
}
}
pluginContext.getLoggerContext().info("Authenticate Test1 userInfo failed");
throw new AuthIncorrectCredentialsException("UserName or Password");
}
@Override
public String name() {
return "Test1";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Oauth方式,适用于辅助登录和直接跳转方式
public class TestPluginAuthService extends AbstractPluginAuthService {
@ConfigKey(ConfigKeyContants.APP_KEY)
protected String appKey;
@ConfigKey(ConfigKeyContants.APP_SECRET)
protected String appSecret;
private final String URL_AUTHORIZE = "https://b2hl7rcn.aliyunidaas.com/login/app/app_mklmfzgdvg4v3gsnjn5ywxpiwe/oauth2/authorize?client_id=%s&redirect_uri=%s&response_type=code&scope=openid&state=%s&prompt=login";
private final String URL_TOKEN = "https://eiam-api-cn-hangzhou.aliyuncs.com/v2/idaas_udovrubrco6sqq7duh6trfhojm/app_mklmfzgdvg4v3gsnjn5ywxpiwe/oauth2/token?grant_type=authorization_code&code=%s&client_id=%s&client_secret=%s&redirect_uri=%s";
private final String URL_USERINFO = "https://eiam-api-cn-hangzhou.aliyuncs.com/v2/idaas_udovrubrco6sqq7duh6trfhojm/app_mklmfzgdvg4v3gsnjn5ywxpiwe/oauth2/userinfo?access_token=%s";
private String LOGIN_TYPE = "Test";
private final String KEY_ACCESS_TOKEN = "access_token";
private String state;
private String redirectUrl = "http://127.0.0.1:8080/auth/token?authType=%s";
@Override
protected AuthInfo doHandle(HttpServletRequest servletRequest, Map<String, String> parameterMap) throws AuthException {
AuthInfo authInfo = new AuthInfo();
//获取access_token
String code = parameterMap.get("Code");
String tokenUrl = String.format(URL_TOKEN, code, appKey, appSecret, String.format(redirectUrl, name()));
String result = pluginContext.getClientContext().doGet(tokenUrl);
if (result == null || result.length() == 0) {
throw new AuthException(name() + " tokenUrl get result is null");
}
JSONObject jsonObject = pluginContext.getDeserializeContext().fromJson(result);
if (jsonObject.containsKey(KEY_ACCESS_TOKEN)) {
String accessToken = jsonObject.getString(KEY_ACCESS_TOKEN);
//获取UserInfo
String userInfoUrl = String.format(URL_USERINFO, accessToken);
result = pluginContext.getClientContext().doGet(userInfoUrl);
if (result == null || result.length() == 0) {
throw new AuthException(name() + " userInfoUrl get result is null");
}
jsonObject = pluginContext.getDeserializeContext().fromJson(result);
authInfo.setUserName(jsonObject.getString("email"));
authInfo.setUserId(jsonObject.getString("sub"));
}
return authInfo;
}
@Override
public String getAuthPageUrl() {
return String.format(URL_AUTHORIZE, appKey, String.format(redirectUrl, name()));
}
@Override
public String name() {
return LOGIN_TYPE;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 插件调试
打包
注意pom.xml的build配置不可更改,否则会出现打包不完整的现象
执行以下命令
mvn clean package -DskipTest=true
1调试
下载调试工程jar (https://lcpapp-static.nos-eastchina1.126.net/auth-plugin/devtools/container.jar)
新建配置文件模板:application.yaml
auth: plugin: list: Test: # 认证插件名称 #插件文件路径 path: D:\workspace\netease\nuims\nuims-plugin-extension\test-plugin\target\test-plugin.jar #插件网络路径,和path二选一 #url: https://lcpapp-static.nos-eastchina1.126.net/auth-plugin/Test-plugin.jar category: main # 有三种类型 main-主登录 sub-辅助登录 direct-直接跳转登录 config: #@configKey的参数默认值 appKey: test appSecret: test
1
2
3
4
5
6
7
8
9
10
11
123.启动命令
java -jar -Dspring.config.location=./application.yaml container.jar
1如果无配置参数可直接启动:
java -jar -Dauth.plugin.list.Test.path=D:\workspace\netease\nuims\nuims-plugin-extension\Test-plugin\target\Test-plugin.jar container.jar
1- 启动成功后,打开浏览器访问 http://127.0.0.1:8080/即可
参数调试
主登录(账号密码)方式 Curl -X POST http://127.0.0.1:8080/auth/token?authType=Test&UserName=test&Password=123456 辅助登录和直接跳转方式,具体参数由认证源规则而定 Curl -X POST http://127.0.0.1:8080/auth/token?authType=Test&Code=**********
1
2
3
4
5
# 插件上传
1、 在账号与权限管理后台,系统管理->插件管理,新增插件
2、更新插件资源,上传调试好的jar文件、js文件(如果不需要可以不传)
3、登录配置,配置相关参数,保存开启
# 插件升级
插件默认版本号为0,如果插件逻辑有改动,则需要复写version()方法,升级版本号,再发布升级
/**
*/
public class TestPluginAuthService extends AbstractPluginAuthService {
@Override
protected AuthInfo doHandle(HttpServletRequest servletRequest, Map<String, String> parameterMap) throws AuthException {
AuthInfo authInfo = new AuthInfo();
return authInfo;
}
@Override
protected void preHandle(Map<String, String> parameterMap) {
logger.info("preHandle");
}
@Override
protected void postHandle(AuthInfo authInfo) {
logger.info("postHandle");
}
@Override
public String getAuthPageUrl() {
return String.format(URL_AUTHORIZE, appKey, String.format(redirectUrl, name()), generateState());
}
@Override
public String name() {
return LOGIN_TYPE;
}
@Override
public int version() {
return 2;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
业务插件,可以更改版本号version,目前不允许向下升级,只能递增版本号升级,比如
1 -> 2
2 -> 3
非法升级
3->2
# 插件使用
应用开启
在IDE登录页组件右侧属性面板开启登录,如果为直接跳转方式,则需要把 “是否开启登录页” 关闭才能选择
如果为用户下沉场景,则需要配置登录方式相关参数,点击发布即可开启成功
# 测试登录
# 常见问题
开发者环境要求
maven、java、node
jdk版本需要指定吗? 使用jdk1.8
# 资源下载
- 模板工程: auth-plugin-archetype (https://lcpapp-static.nos-eastchina1.126.net/auth-plugin/devtools/simple-auth-plugin-archetype.zip)
- 依赖jar文件
- nuims-auth-api-1.0.1.jar(https://lcpapp-static.nos-eastchina1.126.net/auth-plugin/devtools/nuims-auth-api-1.0.1.jar)
- 测试Jar文件:container.jar (https://lcpapp-static.nos-eastchina1.126.net/auth-plugin/devtools/container.jar)
- 示例工程:
- 辅助登录:https://lcpapp-static.nos-eastchina1.126.net/auth-plugin/devtools/sub-plugin.zip
- 主登录:https://lcpapp-static.nos-eastchina1.126.net/auth-plugin/devtools/main-plugin.zip