mirror of
https://github.com/didi/KnowStreaming.git
synced 2026-01-10 17:12:11 +08:00
200 lines
6.9 KiB
Markdown
200 lines
6.9 KiB
Markdown
|
||
|
||

|
||
|
||
## 登录系统对接
|
||
|
||
[KnowStreaming](https://github.com/didi/KnowStreaming)(以下简称KS) 除了实现基于本地MySQL的用户登录认证方式外,还已经实现了基于Ldap的登录认证。
|
||
|
||
但是,登录认证系统并非仅此两种。因此,为了具有更好的拓展性,KS具有自定义登陆认证逻辑,快速对接已有系统的特性。
|
||
|
||
在KS中,我们将登陆认证相关的一些文件放在[km-extends](https://github.com/didi/KnowStreaming/tree/master/km-extends)模块下的[km-account](https://github.com/didi/KnowStreaming/tree/master/km-extends/km-account)模块里。
|
||
|
||
本文将介绍KS如何快速对接自有的用户登录认证系统。
|
||
|
||
### 对接步骤
|
||
|
||
- 创建一个登陆认证类,实现[LogiCommon](https://github.com/didi/LogiCommon)的LoginExtend接口;
|
||
- 将[application.yml](https://github.com/didi/KnowStreaming/blob/master/km-rest/src/main/resources/application.yml)中的spring.logi-security.login-extend-bean-name字段改为登陆认证类的bean名称;
|
||
|
||
```Java
|
||
//LoginExtend 接口
|
||
public interface LoginExtend {
|
||
|
||
/**
|
||
* 验证登录信息,同时记住登录状态
|
||
*/
|
||
UserBriefVO verifyLogin(AccountLoginDTO var1, HttpServletRequest var2, HttpServletResponse var3) throws LogiSecurityException;
|
||
|
||
/**
|
||
* 登出接口,清楚登录状态
|
||
*/
|
||
Result<Boolean> logout(HttpServletRequest var1, HttpServletResponse var2);
|
||
|
||
/**
|
||
* 检查是否已经登录
|
||
*/
|
||
boolean interceptorCheck(HttpServletRequest var1, HttpServletResponse var2, String var3, List<String> var4) throws IOException;
|
||
|
||
}
|
||
|
||
```
|
||
|
||
|
||
|
||
### 对接例子
|
||
|
||
我们以Ldap对接为例,说明KS如何对接登录认证系统。
|
||
|
||
+ 编写[LdapLoginServiceImpl](https://github.com/didi/KnowStreaming/blob/master/km-extends/km-account/src/main/java/com/xiaojukeji/know/streaming/km/account/login/ldap/LdapLoginServiceImpl.java)类,实现LoginExtend接口。
|
||
+ 设置[application.yml](https://github.com/didi/KnowStreaming/blob/master/km-rest/src/main/resources/application.yml)中的spring.logi-security.login-extend-bean-name=ksLdapLoginService。
|
||
|
||
完成上述两步即可实现KS对接Ldap认证登陆。
|
||
|
||
```Java
|
||
@Service("ksLdapLoginService")
|
||
public class LdapLoginServiceImpl implements LoginExtend {
|
||
|
||
|
||
@Override
|
||
public UserBriefVO verifyLogin(AccountLoginDTO loginDTO,
|
||
HttpServletRequest request,
|
||
HttpServletResponse response) throws LogiSecurityException {
|
||
String decodePasswd = AESUtils.decrypt(loginDTO.getPw());
|
||
|
||
// 去LDAP验证账密
|
||
LdapPrincipal ldapAttrsInfo = ldapAuthentication.authenticate(loginDTO.getUserName(), decodePasswd);
|
||
if (ldapAttrsInfo == null) {
|
||
// 用户不存在,正常来说上如果有问题,上一步会直接抛出异常
|
||
throw new LogiSecurityException(ResultCode.USER_NOT_EXISTS);
|
||
}
|
||
|
||
// 进行业务相关操作
|
||
|
||
// 记录登录状态,Ldap因为无法记录登录状态,因此有KnowStreaming进行记录
|
||
initLoginContext(request, response, loginDTO.getUserName(), user.getId());
|
||
return CopyBeanUtil.copy(user, UserBriefVO.class);
|
||
}
|
||
|
||
@Override
|
||
public Result<Boolean> logout(HttpServletRequest request, HttpServletResponse response) {
|
||
|
||
//清理cookie和session
|
||
|
||
return Result.buildSucc(Boolean.TRUE);
|
||
}
|
||
|
||
@Override
|
||
public boolean interceptorCheck(HttpServletRequest request, HttpServletResponse response, String requestMappingValue, List<String> whiteMappingValues) throws IOException {
|
||
|
||
// 检查是否已经登录
|
||
String userName = HttpRequestUtil.getOperator(request);
|
||
if (StringUtils.isEmpty(userName)) {
|
||
// 未登录,则进行登出
|
||
logout(request, response);
|
||
return Boolean.FALSE;
|
||
}
|
||
|
||
return Boolean.TRUE;
|
||
}
|
||
}
|
||
|
||
```
|
||
|
||
|
||
|
||
### 实现原理
|
||
|
||
因为登陆和登出整体实现逻辑是一致的,所以我们以登陆逻辑为例进行介绍。
|
||
|
||
+ 登陆原理
|
||
|
||
登陆走的是[LogiCommon](https://github.com/didi/LogiCommon)自带的LoginController。
|
||
|
||
```java
|
||
@RestController
|
||
public class LoginController {
|
||
|
||
|
||
//登陆接口
|
||
@PostMapping({"/login"})
|
||
public Result<UserBriefVO> login(HttpServletRequest request, HttpServletResponse response, @RequestBody AccountLoginDTO loginDTO) {
|
||
try {
|
||
//登陆认证
|
||
UserBriefVO userBriefVO = this.loginService.verifyLogin(loginDTO, request, response);
|
||
return Result.success(userBriefVO);
|
||
|
||
} catch (LogiSecurityException var5) {
|
||
return Result.fail(var5);
|
||
}
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
而登陆操作是调用LoginServiceImpl类来实现,但是具体由哪个登陆认证类来执行登陆操作却由loginExtendBeanTool来指定。
|
||
|
||
```java
|
||
//LoginServiceImpl类
|
||
@Service
|
||
public class LoginServiceImpl implements LoginService {
|
||
|
||
//实现登陆操作,但是具体哪个登陆类由loginExtendBeanTool来管理
|
||
public UserBriefVO verifyLogin(AccountLoginDTO loginDTO, HttpServletRequest request, HttpServletResponse response) throws LogiSecurityException {
|
||
|
||
return this.loginExtendBeanTool.getLoginExtendImpl().verifyLogin(loginDTO, request, response);
|
||
}
|
||
|
||
|
||
}
|
||
```
|
||
|
||
而loginExtendBeanTool类会优先去查找用户指定的登陆认证类,如果失败则调用默认的登陆认证函数。
|
||
|
||
```java
|
||
//LoginExtendBeanTool类
|
||
@Component("logiSecurityLoginExtendBeanTool")
|
||
public class LoginExtendBeanTool {
|
||
|
||
public LoginExtend getLoginExtendImpl() {
|
||
LoginExtend loginExtend;
|
||
//先调用用户指定登陆类,如果失败则调用系统默认登陆认证
|
||
try {
|
||
//调用的类由spring.logi-security.login-extend-bean-name指定
|
||
loginExtend = this.getCustomLoginExtendImplBean();
|
||
} catch (UnsupportedOperationException var3) {
|
||
loginExtend = this.getDefaultLoginExtendImplBean();
|
||
}
|
||
|
||
return loginExtend;
|
||
}
|
||
}
|
||
```
|
||
|
||
+ 认证原理
|
||
|
||
认证的实现则比较简单,向Spring中注册我们的拦截器PermissionInterceptor。
|
||
|
||
拦截器会调用LoginServiceImpl类的拦截方法,LoginServiceImpl后续处理逻辑就和前面登陆是一致的。
|
||
|
||
```java
|
||
public class PermissionInterceptor implements HandlerInterceptor {
|
||
|
||
|
||
/**
|
||
* 拦截预处理
|
||
* @return boolean false:拦截, 不向下执行, true:放行
|
||
*/
|
||
@Override
|
||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||
|
||
//免登录相关校验,如果验证通过,提前返回
|
||
|
||
//走拦截函数,进行普通用户验证
|
||
return loginService.interceptorCheck(request, response, classRequestMappingValue, whiteMappingValues);
|
||
}
|
||
|
||
}
|
||
```
|
||
|