初始化3.0.0版本

This commit is contained in:
zengqiao
2022-08-18 17:04:05 +08:00
parent 462303fca0
commit 51832385b1
2446 changed files with 93177 additions and 127211 deletions

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xiaojukeji.kafka</groupId>
<artifactId>km-account</artifactId>
<version>${km.revision}</version>
<packaging>jar</packaging>
<parent>
<artifactId>km</artifactId>
<groupId>com.xiaojukeji.kafka</groupId>
<version>${km.revision}</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>com.xiaojukeji.kafka</groupId>
<artifactId>km-common</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.xiaojukeji.kafka</groupId>
<artifactId>km-core</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,53 @@
package com.xiaojukeji.know.streaming.km.account;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Data
@Service
public class KmAccountConfig {
/**
* LoginService的默认配置
*/
@Value(value = "${account.login.service.name:loginService}")
private String loginServiceName;
/**************************************************** Ldap 登录相关配置 ****************************************************/
@Value(value = "${account.ldap.url:}")
private String ldapUrl;
@Value(value = "${account.ldap.basedn:}")
private String ldapBaseDN;
@Value(value = "${account.ldap.factory:}")
private String ldapFactory;
@Value(value = "${account.ldap.filter:}")
private String ldapFilter;
@Value(value = "${account.ldap.security.authentication:}")
private String securityAuthentication;
@Value(value = "${account.ldap.security.principal:}")
private String securityPrincipal;
@Value(value = "${account.ldap.security.credentials:}")
private String securityCredentials;
/**************************************************** Ldap 用户注册到KM相关 ****************************************************/
@Value(value = "${account.ldap.auth-user-registration-role:0,1,2}")
private String authUserRegistrationRole; // ldap自动注册的默认角色。请注意它通常来说都是低权限角色
@Value(value = "${account.ldap.auth-user-registration:false}")
private Boolean authUserRegistration; // ldap自动注册是否开启
private KmAccountConfig() {
}
}

View File

@@ -0,0 +1,25 @@
package com.xiaojukeji.know.streaming.km.account.common.bizenum;
import lombok.Getter;
@Getter
public enum LoginServiceNameEnum {
DEFAULT(LoginServiceNameEnum.DEFAULT_LOGIN_NAME, "默认"),
LDAP(LoginServiceNameEnum.LDAP_LOGIN_NAME, "Ldap登录"),
;
public static final String DEFAULT_LOGIN_NAME = "loginService";
public static final String LDAP_LOGIN_NAME = "ldapLoginService";
private final String name;
private final String msg;
LoginServiceNameEnum(String name, String msg) {
this.name = name;
this.msg = msg;
}
}

View File

@@ -0,0 +1,24 @@
package com.xiaojukeji.know.streaming.km.account.common.constant;
public class TrickJumpLoginConstant {
/**
* HTTP Header key
*/
public static final String TRICK_JUMP_LOGIN_SWITCH = "Trick-Login-Switch";
public static final String TRICK_JUMP_LOGIN_USER = "Trick-Login-User";
/**
* 配置允许 trick 登录用户名单
*/
public static final String TRICK_JUMP_LOGIN_LEGAL_USER_CONFIG_GROUP = "SECURITY.LOGIN";
public static final String TRICK_JUMP_LOGIN_LEGAL_USER_CONFIG_NAME = "SECURITY.TRICK_USERS";
/**
* 开关状态值
*/
public static final String TRICK_JUMP_LOGIN_SWITCH_ON = "on";
private TrickJumpLoginConstant() {
}
}

View File

@@ -0,0 +1,18 @@
package com.xiaojukeji.know.streaming.km.account.common.ldap;
import lombok.Data;
@Data
public class LdapPrincipal {
private String userDN;
private String sAMAccountName;
private String department;
private String company;
private String displayName;
private String mail;
}

View File

@@ -0,0 +1,16 @@
package com.xiaojukeji.know.streaming.km.account.common.ldap.exception;
import com.didiglobal.logi.security.exception.CodeMsg;
public class LdapException extends RuntimeException {
public LdapException() {}
public LdapException(CodeMsg codeMsg) {
super(codeMsg.getCode() + "-" + codeMsg.getMessage());
}
public LdapException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,147 @@
package com.xiaojukeji.know.streaming.km.account.login.ldap;
import com.didiglobal.logi.security.common.Result;
import com.didiglobal.logi.security.common.dto.account.AccountLoginDTO;
import com.didiglobal.logi.security.common.dto.user.UserDTO;
import com.didiglobal.logi.security.common.entity.user.User;
import com.didiglobal.logi.security.common.enums.ResultCode;
import com.didiglobal.logi.security.common.vo.user.UserBriefVO;
import com.didiglobal.logi.security.exception.LogiSecurityException;
import com.didiglobal.logi.security.service.LoginService;
import com.didiglobal.logi.security.service.UserService;
import com.didiglobal.logi.security.util.AESUtils;
import com.didiglobal.logi.security.util.CopyBeanUtil;
import com.didiglobal.logi.security.util.HttpRequestUtil;
import com.xiaojukeji.know.streaming.km.account.KmAccountConfig;
import com.xiaojukeji.know.streaming.km.account.common.bizenum.LoginServiceNameEnum;
import com.xiaojukeji.know.streaming.km.account.common.ldap.LdapPrincipal;
import com.xiaojukeji.know.streaming.km.account.login.ldap.remote.LdapAuthentication;
import com.xiaojukeji.know.streaming.km.common.utils.CommonUtils;
import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil;
import com.xiaojukeji.know.streaming.km.common.utils.ValidateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
import static com.didiglobal.logi.security.util.HttpRequestUtil.*;
import static com.didiglobal.logi.security.util.HttpRequestUtil.COOKIE_OR_SESSION_MAX_AGE_UNIT_SEC;
/**
* @author Hu.Yue
* @date 2021/8/4
*/
//@Service(LoginServiceNameEnum.LDAP_LOGIN_NAME)
public class LdapLoginServiceImpl implements LoginService {
private static final Logger LOGGER = LoggerFactory.getLogger(LdapLoginServiceImpl.class);
@Autowired
private UserService userService;
@Autowired
private KmAccountConfig kmAccountConfig;
@Autowired
private LdapAuthentication ldapAuthentication;
@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);
}
// 如果用户不存在,则进行插入
User user = userService.getUserByUserName(ldapAttrsInfo.getSAMAccountName());
if(ValidateUtils.isNull(user) && kmAccountConfig.getAuthUserRegistration() != null && kmAccountConfig.getAuthUserRegistration()) {
// 自动注册用户
UserDTO userDTO = new UserDTO();
userDTO.setUserName(ldapAttrsInfo.getSAMAccountName());
userDTO.setPw(decodePasswd);
userDTO.setRealName(ldapAttrsInfo.getDisplayName());
userDTO.setPhone("");
userDTO.setEmail(ldapAttrsInfo.getMail());
userDTO.setRoleIds(CommonUtils.string2IntList(kmAccountConfig.getAuthUserRegistrationRole()));
userService.addUser(userDTO, ldapAttrsInfo.getSAMAccountName());
// user赋值
user = ConvertUtil.obj2Obj(userDTO, User.class);
}
// 记录登录状态
initLoginContext(request, response, loginDTO.getUserName(), user.getId());
return CopyBeanUtil.copy(user, UserBriefVO.class);
}
@Override
public Result<Boolean> logout(HttpServletRequest request, HttpServletResponse response){
request.getSession().invalidate();
response.setStatus(REDIRECT_CODE);
return Result.buildSucc(Boolean.TRUE);
}
@Override
public boolean interceptorCheck(HttpServletRequest request, HttpServletResponse response, String requestMappingValue, List<String> whiteMappingValues) throws IOException {
if (StringUtils.isEmpty(requestMappingValue)) {
LOGGER.error("method=interceptorCheck||msg=uri illegal||uri={}", request.getRequestURI());
return Boolean.FALSE;
}
// 白名单接口
for(String mapping : whiteMappingValues){
if (requestMappingValue.contains(mapping)){
return Boolean.TRUE;
}
}
// 检查是否已经登录
String userName = HttpRequestUtil.getOperator(request);
if(StringUtils.isEmpty(userName)) {
// 未登录
logout(request, response);
return Boolean.FALSE;
}
// 检查用户是否存在
User user = userService.getUserByUserName(userName);
if(user == null) {
throw new LogiSecurityException(ResultCode.USER_NOT_EXISTS);
}
initLoginContext(request, response, userName, user.getId());
return Boolean.TRUE;
}
private void initLoginContext(HttpServletRequest request, HttpServletResponse response, String userName, Integer userId) {
HttpSession session = request.getSession(true);
session.setMaxInactiveInterval( COOKIE_OR_SESSION_MAX_AGE_UNIT_SEC );
session.setAttribute(USER, userName);
session.setAttribute(USER_ID, userId);
Cookie cookieUserName = new Cookie(USER, userName);
cookieUserName.setMaxAge(COOKIE_OR_SESSION_MAX_AGE_UNIT_SEC);
cookieUserName.setPath("/");
Cookie cookieUserId = new Cookie(USER_ID, userId.toString());
cookieUserId.setMaxAge(COOKIE_OR_SESSION_MAX_AGE_UNIT_SEC);
cookieUserId.setPath("/");
response.addCookie(cookieUserName);
response.addCookie(cookieUserId);
}
}

View File

@@ -0,0 +1,144 @@
package com.xiaojukeji.know.streaming.km.account.login.ldap.remote;
import com.didiglobal.logi.security.common.enums.ResultCode;
import com.didiglobal.logi.security.exception.LogiSecurityException;
import com.xiaojukeji.know.streaming.km.account.KmAccountConfig;
import com.xiaojukeji.know.streaming.km.account.common.ldap.LdapPrincipal;
import com.xiaojukeji.know.streaming.km.account.common.ldap.exception.LdapException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import java.util.Hashtable;
/**
* @author Hu.Yue
* @date 2021/8/4
**/
@Component
public class LdapAuthentication {
private static final Logger LOGGER = LoggerFactory.getLogger(LdapAuthentication.class);
@Autowired
private KmAccountConfig kmAccountConfig;
/**
* LDAP账密验证
*/
public LdapPrincipal authenticate(String userName, String password) throws LogiSecurityException, LdapException {
// 获取ldap-context
LdapContext ctx = getLdapContext();
// 获取用户信息
LdapPrincipal ldapAttrsInfo = getLdapAttrsInfo(userName, ctx);
// 校验密码
try {
// 校验密码
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, ldapAttrsInfo.getUserDN());
ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password);
ctx.reconnect(null);
// 返回用户信息
return ldapAttrsInfo;
} catch (Exception e) {
LOGGER.error("method=authenticate||userName={}||errMsg={}", userName, e);
// 密码错误
throw new LogiSecurityException(ResultCode.USER_CREDENTIALS_ERROR);
} finally {
this.closeLdapContext(ctx);
}
}
/**************************************************** private method ****************************************************/
private LdapContext getLdapContext() throws LdapException {
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, kmAccountConfig.getLdapFactory());
env.put(Context.PROVIDER_URL, kmAccountConfig.getLdapUrl() + kmAccountConfig.getLdapBaseDN());
env.put(Context.SECURITY_AUTHENTICATION, kmAccountConfig.getSecurityAuthentication());
// 此处若不指定用户名和密码, 则自动转换为匿名登录
env.put(Context.SECURITY_PRINCIPAL, kmAccountConfig.getSecurityPrincipal());
env.put(Context.SECURITY_CREDENTIALS, kmAccountConfig.getSecurityCredentials());
try {
return new InitialLdapContext(env, null);
} catch (Exception e) {
LOGGER.error("method=getLdapContext||errMsg=exception", e);
throw new LdapException("调用Ldap服务异常", e);
}
}
/**
* 获取用户信息
*/
private LdapPrincipal getLdapAttrsInfo(String userName, LdapContext ctx) {
//存储更多的LDAP元信息
try {
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
// 查找
NamingEnumeration<SearchResult> en = ctx.search(
"",
"(&(objectClass=*)(" + kmAccountConfig.getLdapFilter() + "=" + userName + "))",
constraints
);
if (en == null || !en.hasMoreElements()) {
// 用户不存在
throw new LogiSecurityException(ResultCode.USER_NOT_EXISTS);
}
// maybe more than one element
while (en.hasMoreElements()) {
Object obj = en.nextElement();
if (obj instanceof SearchResult) {
SearchResult si = (SearchResult) obj;
// 携带LDAP更多元信息以填充用户元信息
LdapPrincipal ldapPrincipal = new LdapPrincipal();
ldapPrincipal.setUserDN(si.getName() + "," + kmAccountConfig.getLdapBaseDN());
ldapPrincipal.setSAMAccountName(this.keyValueSplit(si.getAttributes().get("samaccountname").toString()));
ldapPrincipal.setDepartment(this.keyValueSplit(si.getAttributes().get("department").toString()));
ldapPrincipal.setCompany(this.keyValueSplit(si.getAttributes().get("company").toString()));
ldapPrincipal.setDisplayName(this.keyValueSplit(si.getAttributes().get("displayname").toString()));
ldapPrincipal.setMail(this.keyValueSplit(si.getAttributes().get("mail").toString()));
return ldapPrincipal;
}
}
// 用户不存在
throw new LogiSecurityException(ResultCode.USER_NOT_EXISTS);
} catch (Exception e) {
LOGGER.error("method=getLdapAttrsInfo||userName={}||errMsg=exception", userName, e);
throw new LdapException("调用Ldap服务异常", e);
}
}
private void closeLdapContext(LdapContext ctx) {
if (ctx == null) {
return;
}
try {
ctx.close();
} catch (Exception e) {
LOGGER.error("method=closeLdapContext||errMsg=exception", e);
}
}
public String keyValueSplit(String keyValue){
return keyValue.split(":\\s+")[1];
}
}

View File

@@ -0,0 +1,19 @@
package com.xiaojukeji.know.streaming.km.account.login.trick;
import javax.servlet.http.HttpServletRequest;
/**
* @author zengqiao
* @date 21/5/18
*/
public interface TrickJumpLoginService {
/**
* 是否开启trick的方式登录
*/
boolean isOpenTrickJumpLogin(HttpServletRequest request);
/**
* 开启trick方式登录后当前用户是否可以登录
*/
String checkTrickJumpLogin(HttpServletRequest request);
}

View File

@@ -0,0 +1,71 @@
package com.xiaojukeji.know.streaming.km.account.login.trick.impl;
import com.didiglobal.logi.security.common.vo.config.ConfigVO;
import com.didiglobal.logi.security.service.ConfigService;
import com.xiaojukeji.know.streaming.km.account.common.constant.TrickJumpLoginConstant;
import com.xiaojukeji.know.streaming.km.account.login.trick.TrickJumpLoginService;
import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil;
import com.xiaojukeji.know.streaming.km.common.utils.ValidateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author zengqiao
* @date 21/5/18
*/
@Service
public class TrickJumpLoginServiceImpl implements TrickJumpLoginService {
private static final Logger LOGGER = LoggerFactory.getLogger(TrickJumpLoginServiceImpl.class);
@Autowired
private ConfigService configService;
@Override
public boolean isOpenTrickJumpLogin(HttpServletRequest request) {
return TrickJumpLoginConstant.TRICK_JUMP_LOGIN_SWITCH_ON.equals(request.getHeader(TrickJumpLoginConstant.TRICK_JUMP_LOGIN_SWITCH));
}
@Override
public String checkTrickJumpLogin(HttpServletRequest request) {
String trickLoginUser = request.getHeader(TrickJumpLoginConstant.TRICK_JUMP_LOGIN_USER);
LOGGER.info("method=checkTrickJumpLogin||userName={}||uri={}||msg=try trick jump login", trickLoginUser, request.getRequestURI());
if (!checkTrickJumpLogin(trickLoginUser)) {
LOGGER.warn("method=checkTrickJumpLogin||userName={}||uri={}||msg=trick login failed", trickLoginUser, request.getRequestURI());
return null;
}
return trickLoginUser;
}
private boolean checkTrickJumpLogin(String trickJumpLoginUser) {
List<ConfigVO> voList = configService.listConfigByGroup(TrickJumpLoginConstant.TRICK_JUMP_LOGIN_LEGAL_USER_CONFIG_GROUP)
.stream()
.filter(elem -> elem.getValueName().equals(TrickJumpLoginConstant.TRICK_JUMP_LOGIN_LEGAL_USER_CONFIG_NAME))
.collect(Collectors.toList());
if (ValidateUtils.isEmptyList(voList)) {
// 不存在
return false;
}
for (ConfigVO vo: voList) {
List<String> canJumpUserNameList = ConvertUtil.str2ObjArrayByJson(vo.getValue(), String.class);
if (ValidateUtils.isEmptyList(canJumpUserNameList)) {
continue;
}
if (canJumpUserNameList.contains(trickJumpLoginUser)) {
return true;
}
}
return false;
}
}