From cf7bc11cbd30237bd0b30e9dd7ea96c47a455472 Mon Sep 17 00:00:00 2001 From: zengqiao Date: Thu, 8 Sep 2022 13:46:45 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=99=BB=E5=BD=95=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E5=AF=B9=E6=8E=A5=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/dev_guide/登录系统对接.md | 199 +++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 docs/dev_guide/登录系统对接.md diff --git a/docs/dev_guide/登录系统对接.md b/docs/dev_guide/登录系统对接.md new file mode 100644 index 00000000..e1038c63 --- /dev/null +++ b/docs/dev_guide/登录系统对接.md @@ -0,0 +1,199 @@ + + +![Logo](https://user-images.githubusercontent.com/71620349/185368586-aed82d30-1534-453d-86ff-ecfa9d0f35bd.png) + +## 登录系统对接 + +[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 logout(HttpServletRequest var1, HttpServletResponse var2); + + /** + * 检查是否已经登录 + */ + boolean interceptorCheck(HttpServletRequest var1, HttpServletResponse var2, String var3, List 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 logout(HttpServletRequest request, HttpServletResponse response) { + + //清理cookie和session + + return Result.buildSucc(Boolean.TRUE); + } + + @Override + public boolean interceptorCheck(HttpServletRequest request, HttpServletResponse response, String requestMappingValue, List 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 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); + } + +} +``` +