kafka-manager 2.0

This commit is contained in:
zengqiao
2020-09-28 15:46:34 +08:00
parent 28d985aaf1
commit c6e4b60424
1253 changed files with 82183 additions and 37179 deletions

View File

@@ -0,0 +1,42 @@
<?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>
<artifactId>kafka-manager-account</artifactId>
<version>2.0.0-SNAPSHOT</version>
<parent>
<artifactId>kafka-manager</artifactId>
<groupId>com.xiaojukeji.kafka</groupId>
<version>2.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>com.xiaojukeji.kafka</groupId>
<artifactId>kafka-manager-common</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.xiaojukeji.kafka</groupId>
<artifactId>kafka-manager-dao</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>com.xiaojukeji.kafka</groupId>
<artifactId>kafka-manager-core</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,36 @@
package com.xiaojukeji.kafka.manager.account;
import com.xiaojukeji.kafka.manager.account.common.EnterpriseStaff;
import com.xiaojukeji.kafka.manager.common.bizenum.AccountRoleEnum;
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
import com.xiaojukeji.kafka.manager.common.entity.ao.account.Account;
import com.xiaojukeji.kafka.manager.common.entity.pojo.AccountDO;
import java.util.List;
import java.util.Map;
/**
* @author huangyiminghappy@163.com
* @date 2019-04-26
*/
public interface AccountService {
ResultStatus createAccount(AccountDO accountDO);
AccountDO getAccountDO(String username);
ResultStatus deleteByName(String username);
ResultStatus updateAccount(AccountDO accountDO);
List<AccountDO> list();
List<EnterpriseStaff> searchAccountByPrefix(String prefix);
AccountRoleEnum getAccountRoleFromCache(String username);
Account getAccountFromCache(String userName);
boolean isAdminOrderHandler(String username);
List<Account> getAdminOrderHandlerFromCache();
}

View File

@@ -0,0 +1,19 @@
package com.xiaojukeji.kafka.manager.account;
import com.xiaojukeji.kafka.manager.common.entity.ao.account.Account;
import com.xiaojukeji.kafka.manager.common.entity.dto.normal.LoginDTO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author zengqiao
* @date 20/8/20
*/
public interface LoginService {
Account login(HttpServletRequest request, HttpServletResponse response, LoginDTO dto);
void logout(HttpServletRequest request, HttpServletResponse response, Boolean needJump2LoginPage);
boolean checkLogin(HttpServletRequest request, HttpServletResponse response);
}

View File

@@ -0,0 +1,53 @@
package com.xiaojukeji.kafka.manager.account.common;
/**
* 企业员工信息
* @author zengqiao
* @date 20/8/26
*/
public class EnterpriseStaff {
private String username;
private String chineseName;
private String department;
public EnterpriseStaff(String username, String chineseName, String department) {
this.username = username;
this.chineseName = chineseName;
this.department = department;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getChineseName() {
return chineseName;
}
public void setChineseName(String chineseName) {
this.chineseName = chineseName;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
@Override
public String toString() {
return "EnterpriseStaff{" +
"username='" + username + '\'' +
", chineseName='" + chineseName + '\'' +
", department='" + department + '\'' +
'}';
}
}

View File

@@ -0,0 +1,16 @@
package com.xiaojukeji.kafka.manager.account.component;
import com.xiaojukeji.kafka.manager.account.common.EnterpriseStaff;
import java.util.List;
/**
* 企业员工组织信息
* @author zengqiao
* @date 20/8/20
*/
public abstract class AbstractEnterpriseStaffService {
public abstract EnterpriseStaff getEnterpriseStaffByName(String username);
public abstract List<EnterpriseStaff> searchEnterpriseStaffByKeyWord(String keyWord);
}

View File

@@ -0,0 +1,28 @@
package com.xiaojukeji.kafka.manager.account.component;
import com.xiaojukeji.kafka.manager.common.entity.dto.normal.LoginDTO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 单点登录抽象类
* @author zengqiao
* @date 20/8/20
*/
public abstract class AbstractSingleSignOn {
/**
* HttpServletResponse.SC_UNAUTHORIZED
*/
protected static final Integer REDIRECT_CODE = 401;
protected static final String HEADER_REDIRECT_KEY = "location";
public abstract String loginAndGetLdap(HttpServletRequest request, HttpServletResponse response, LoginDTO dto);
public abstract void logout(HttpServletRequest request, HttpServletResponse response, Boolean needJump2LoginPage);
public abstract String checkLoginAndGetLdap(HttpServletRequest request);
public abstract void setRedirectToLoginPage(HttpServletResponse response);
}

View File

@@ -0,0 +1,58 @@
package com.xiaojukeji.kafka.manager.account.component.account;
import com.xiaojukeji.kafka.manager.account.common.EnterpriseStaff;
import com.xiaojukeji.kafka.manager.account.component.AbstractEnterpriseStaffService;
import com.xiaojukeji.kafka.manager.common.entity.pojo.AccountDO;
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
import com.xiaojukeji.kafka.manager.dao.AccountDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @author zengqiao
* @date 20/8/20
*/
@Service("enterpriseStaffService")
public class BaseEnterpriseStaffService extends AbstractEnterpriseStaffService {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseEnterpriseStaffService.class);
@Autowired
private AccountDao accountDao;
@Override
public EnterpriseStaff getEnterpriseStaffByName(String username) {
try {
AccountDO accountDO = accountDao.getByName(username);
if (ValidateUtils.isNull(accountDO)) {
return null;
}
return new EnterpriseStaff(accountDO.getUsername(), accountDO.getUsername(), "");
} catch (Exception e) {
LOGGER.error("get enterprise staff info failed, username:{}.", username, e);
}
return null;
}
@Override
public List<EnterpriseStaff> searchEnterpriseStaffByKeyWord(String keyWord) {
try {
List<AccountDO> doList = accountDao.searchByNamePrefix(keyWord);
if (ValidateUtils.isEmptyList(doList)) {
return new ArrayList<>();
}
List<EnterpriseStaff> staffList = new ArrayList<>();
for (AccountDO accountDO: doList) {
staffList.add(new EnterpriseStaff(accountDO.getUsername(), accountDO.getUsername(), ""));
}
return staffList;
} catch (Exception e) {
LOGGER.error("search enterprise staff info failed, prefix:{}.", keyWord, e);
}
return new ArrayList<>();
}
}

View File

@@ -0,0 +1,63 @@
package com.xiaojukeji.kafka.manager.account.component.sso;
import com.xiaojukeji.kafka.manager.account.AccountService;
import com.xiaojukeji.kafka.manager.account.component.AbstractSingleSignOn;
import com.xiaojukeji.kafka.manager.common.constant.LoginConstant;
import com.xiaojukeji.kafka.manager.common.entity.dto.normal.LoginDTO;
import com.xiaojukeji.kafka.manager.common.entity.pojo.AccountDO;
import com.xiaojukeji.kafka.manager.common.utils.EncryptUtil;
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author zengqiao
* @date 20/8/20
*/
@Service("singleSignOn")
public class BaseSessionSignOn extends AbstractSingleSignOn {
@Autowired
private AccountService accountService;
@Override
public String loginAndGetLdap(HttpServletRequest request, HttpServletResponse response, LoginDTO dto) {
if (ValidateUtils.isBlank(dto.getUsername()) || ValidateUtils.isNull(dto.getPassword())) {
return null;
}
AccountDO accountDO = accountService.getAccountDO(dto.getUsername());
if (ValidateUtils.isNull(accountDO)) {
return null;
}
if (!accountDO.getPassword().equals(EncryptUtil.md5(dto.getPassword()))) {
return null;
}
return dto.getUsername();
}
@Override
public void logout(HttpServletRequest request, HttpServletResponse response, Boolean needJump2LoginPage) {
request.getSession().invalidate();
if (needJump2LoginPage) {
response.setStatus(AbstractSingleSignOn.REDIRECT_CODE);
response.addHeader(AbstractSingleSignOn.HEADER_REDIRECT_KEY, "");
}
}
@Override
public String checkLoginAndGetLdap(HttpServletRequest request) {
Object username = request.getSession().getAttribute(LoginConstant.SESSION_USERNAME_KEY);
if (ValidateUtils.isNull(username)) {
return null;
}
return (String) username;
}
@Override
public void setRedirectToLoginPage(HttpServletResponse response) {
response.setStatus(AbstractSingleSignOn.REDIRECT_CODE);
response.addHeader(AbstractSingleSignOn.HEADER_REDIRECT_KEY, "");
}
}

View File

@@ -0,0 +1,272 @@
package com.xiaojukeji.kafka.manager.account.impl;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.xiaojukeji.kafka.manager.account.AccountService;
import com.xiaojukeji.kafka.manager.account.common.EnterpriseStaff;
import com.xiaojukeji.kafka.manager.account.component.AbstractEnterpriseStaffService;
import com.xiaojukeji.kafka.manager.common.bizenum.AccountRoleEnum;
import com.xiaojukeji.kafka.manager.common.constant.Constant;
import com.xiaojukeji.kafka.manager.common.entity.ResultStatus;
import com.xiaojukeji.kafka.manager.common.entity.ao.account.Account;
import com.xiaojukeji.kafka.manager.common.entity.pojo.AccountDO;
import com.xiaojukeji.kafka.manager.common.utils.EncryptUtil;
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
import com.xiaojukeji.kafka.manager.dao.AccountDao;
import com.xiaojukeji.kafka.manager.service.service.ConfigService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* @author huangyiminghappy@163.com
* @date 2019-04-26
*/
@Service("accountService")
public class AccountServiceImpl implements AccountService {
private static final Logger LOGGER = LoggerFactory.getLogger(AccountServiceImpl.class);
private static final String ADMIN_ORDER_HANDLER_CONFIG_KEY = "ADMIN_ORDER_HANDLER_CONFIG";
@Autowired
private AccountDao accountDao;
@Autowired
private ConfigService configService;
@Autowired
private AbstractEnterpriseStaffService enterpriseStaffService;
/**
* 用户组织信息
* <username, Staff>
*/
private static final Cache<String, EnterpriseStaff> USERNAME_STAFF_CACHE = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterAccess(600, TimeUnit.MINUTES)
.expireAfterWrite(600, TimeUnit.MINUTES).build();
/**
* 用户角色信息
*/
private volatile Map<String, AccountRoleEnum> ACCOUNT_ROLE_CACHE = null;
private volatile List<String> ADMIN_ORDER_HANDLER_CACHE = null;
@Override
public ResultStatus createAccount(AccountDO accountDO) {
try {
accountDO.setPassword(EncryptUtil.md5(accountDO.getPassword()));
if (accountDao.addNewAccount(accountDO) > 0) {
return ResultStatus.SUCCESS;
}
} catch (DuplicateKeyException e) {
LOGGER.info("create account failed, account already existed, accountDO:{}.", accountDO, e);
return ResultStatus.RESOURCE_ALREADY_EXISTED;
} catch (Exception e) {
LOGGER.error("create account failed, operate mysql failed, accountDO:{}.", accountDO, e);
}
return ResultStatus.MYSQL_ERROR;
}
@Override
public ResultStatus deleteByName(String username) {
try {
if (accountDao.deleteByName(username) > 0) {
return ResultStatus.SUCCESS;
}
} catch (Exception e) {
LOGGER.error("delete account failed, username:{}.", username, e);
}
return ResultStatus.MYSQL_ERROR;
}
@Override
public ResultStatus updateAccount(AccountDO accountDO) {
try {
AccountDO oldAccountDO = accountDao.getByName(accountDO.getUsername());
if (ValidateUtils.isNull(oldAccountDO)) {
return ResultStatus.ACCOUNT_NOT_EXIST;
}
if (!ValidateUtils.isNull(accountDO.getPassword())) {
accountDO.setPassword(EncryptUtil.md5(accountDO.getPassword()));
} else {
accountDO.setPassword(oldAccountDO.getPassword());
}
if (accountDao.updateByName(accountDO) > 0) {
return ResultStatus.SUCCESS;
}
} catch (Exception e) {
LOGGER.error("update account failed, accountDO:{}.", accountDO, e);
}
return ResultStatus.MYSQL_ERROR;
}
@Override
public AccountDO getAccountDO(String username) {
return accountDao.getByName(username);
}
@Override
public List<AccountDO> list() {
return accountDao.list();
}
private EnterpriseStaff getStaffData(String username) {
EnterpriseStaff enterpriseStaff = USERNAME_STAFF_CACHE.getIfPresent(username);
if (!ValidateUtils.isNull(enterpriseStaff)) {
return enterpriseStaff;
}
enterpriseStaff = enterpriseStaffService.getEnterpriseStaffByName(username);
if (ValidateUtils.isNull(enterpriseStaff)) {
return null;
}
USERNAME_STAFF_CACHE.put(username, enterpriseStaff);
return enterpriseStaff;
}
@Override
public AccountRoleEnum getAccountRoleFromCache(String username) {
if (ValidateUtils.isNull(ACCOUNT_ROLE_CACHE)) {
flush();
}
return ACCOUNT_ROLE_CACHE.getOrDefault(username, AccountRoleEnum.NORMAL);
}
private Map<String, AccountRoleEnum> getAdminRoleEnum() {
Map<String, AccountRoleEnum> ldapMap = new HashMap<>();
if (ValidateUtils.isNull(ACCOUNT_ROLE_CACHE)) {
flush();
}
for (Map.Entry<String, AccountRoleEnum> entry : ACCOUNT_ROLE_CACHE.entrySet()) {
if (!AccountRoleEnum.OP.equals(entry.getValue()) &&
!AccountRoleEnum.RD.equals(entry.getValue())) {
continue;
}
ldapMap.put(entry.getKey(), entry.getValue());
}
return ldapMap;
}
@Override
public Account getAccountFromCache(String username) {
Account account = new Account();
account.setUsername(username);
if (Constant.AUTO_HANDLE_USER_NAME.equals(username)) {
account.setChineseName(Constant.AUTO_HANDLE_CHINESE_NAME);
account.setAccountRoleEnum(AccountRoleEnum.OP);
return account;
}
AccountRoleEnum roleEnum = this.getAccountRoleFromCache(username);
account.setAccountRoleEnum(roleEnum);
EnterpriseStaff enterpriseStaff = this.getStaffData(username);
if (ValidateUtils.isNull(enterpriseStaff)) {
account.setChineseName(username);
return account;
}
account.setDepartment(enterpriseStaff.getDepartment());
account.setChineseName(enterpriseStaff.getChineseName());
return account;
}
private List<Account> getOPAccountsFromCache() {
List<Account> accountList = new ArrayList<>();
for (Map.Entry<String, AccountRoleEnum> entry : getAdminRoleEnum().entrySet()) {
AccountRoleEnum role = entry.getValue();
if (!AccountRoleEnum.OP.getRole().equals(role.getRole())) {
continue;
}
Account account = this.getAccountFromCache(entry.getKey());
if (ValidateUtils.isNull(account)) {
continue;
}
accountList.add(account);
}
return accountList;
}
private boolean isOp(String username) {
if (ValidateUtils.isNull(ACCOUNT_ROLE_CACHE)) {
flush();
}
AccountRoleEnum accountRoleEnum = ACCOUNT_ROLE_CACHE.getOrDefault(username, AccountRoleEnum.NORMAL);
if (accountRoleEnum.equals(AccountRoleEnum.OP)) {
return true;
}
return false;
}
@Override
public boolean isAdminOrderHandler(String username) {
if (isOp(username)) {
return true;
}
if (!ValidateUtils.isEmptyList(ADMIN_ORDER_HANDLER_CACHE)
&& ADMIN_ORDER_HANDLER_CACHE.contains(username)) {
return true;
}
return false;
}
@Override
public List<Account> getAdminOrderHandlerFromCache() {
if (ValidateUtils.isEmptyList(ADMIN_ORDER_HANDLER_CACHE)) {
return getOPAccountsFromCache();
}
List<Account> accountList = new ArrayList<>();
for (String ldap : ADMIN_ORDER_HANDLER_CACHE) {
Account account = this.getAccountFromCache(ldap);
if (ValidateUtils.isNull(account)) {
continue;
}
accountList.add(account);
}
return accountList;
}
@Override
public List<EnterpriseStaff> searchAccountByPrefix(String prefix) {
return enterpriseStaffService.searchEnterpriseStaffByKeyWord(prefix);
}
@Scheduled(cron ="0/5 * * * * ?")
public void flush() {
try {
ADMIN_ORDER_HANDLER_CACHE =
configService.getArrayByKey(ADMIN_ORDER_HANDLER_CONFIG_KEY, String.class);
} catch (Exception e) {
LOGGER.error("flush handler account failed.", e);
}
try {
List<AccountDO> doList = accountDao.list();
if (ValidateUtils.isNull(doList)) {
doList = new ArrayList<>();
}
Map<String, AccountRoleEnum> tempMap = new ConcurrentHashMap<>(doList.size());
for (AccountDO accountDO: doList) {
tempMap.put(accountDO.getUsername(), AccountRoleEnum.getUserRoleEnum(accountDO.getRole()));
}
ACCOUNT_ROLE_CACHE = tempMap;
} catch (Exception e) {
LOGGER.error("flush account failed.", e);
}
}
}

View File

@@ -0,0 +1,109 @@
package com.xiaojukeji.kafka.manager.account.impl;
import com.xiaojukeji.kafka.manager.account.AccountService;
import com.xiaojukeji.kafka.manager.account.component.AbstractSingleSignOn;
import com.xiaojukeji.kafka.manager.account.LoginService;
import com.xiaojukeji.kafka.manager.common.bizenum.AccountRoleEnum;
import com.xiaojukeji.kafka.manager.common.constant.ApiPrefix;
import com.xiaojukeji.kafka.manager.common.constant.LoginConstant;
import com.xiaojukeji.kafka.manager.common.entity.ao.account.Account;
import com.xiaojukeji.kafka.manager.common.entity.dto.normal.LoginDTO;
import com.xiaojukeji.kafka.manager.common.utils.ValidateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* @author zengqiao
* @date 20/8/20
*/
@Service("loginService")
public class LoginServiceImpl implements LoginService {
private final static Logger LOGGER = LoggerFactory.getLogger(LoginServiceImpl.class);
@Autowired
private AccountService accountService;
@Autowired
private AbstractSingleSignOn singleSignOn;
@Override
public Account login(HttpServletRequest request, HttpServletResponse response, LoginDTO loginDTO) {
String username = singleSignOn.loginAndGetLdap(request, response, loginDTO);
if (ValidateUtils.isBlank(username)) {
logout(request, response, false);
return null;
}
Account account = accountService.getAccountFromCache(username);
initLoginContext(request, response, account);
return account;
}
private void initLoginContext(HttpServletRequest request, HttpServletResponse response, Account account) {
HttpSession session = request.getSession(true);
session.setMaxInactiveInterval(LoginConstant.COOKIE_OR_SESSION_MAX_AGE_UNIT_MS);
session.setAttribute(LoginConstant.SESSION_USERNAME_KEY, account.getUsername());
Cookie cookieChineseName = new Cookie(LoginConstant.COOKIE_CHINESE_USERNAME_KEY, account.getChineseName());
cookieChineseName.setMaxAge(LoginConstant.COOKIE_OR_SESSION_MAX_AGE_UNIT_MS);
cookieChineseName.setPath("/");
response.addCookie(cookieChineseName);
}
@Override
public void logout(HttpServletRequest request, HttpServletResponse response, Boolean needJump2LoginPage) {
singleSignOn.logout(request, response, needJump2LoginPage);
}
@Override
public boolean checkLogin(HttpServletRequest request, HttpServletResponse response) {
String uri = request.getRequestURI();
if (!(uri.contains(ApiPrefix.API_V1_NORMAL_PREFIX)
|| uri.contains(ApiPrefix.API_V1_RD_PREFIX)
|| uri.contains(ApiPrefix.API_V1_OP_PREFIX))) {
// 白名单接口, 直接忽略登录
return true;
}
String username = singleSignOn.checkLoginAndGetLdap(request);
if (ValidateUtils.isBlank(username)) {
// 未登录, 则返回false, 同时重定向到登录页面
singleSignOn.setRedirectToLoginPage(response);
return false;
}
boolean status = checkAuthority(request, accountService.getAccountRoleFromCache(username));
if (status) {
HttpSession session = request.getSession();
session.setAttribute(LoginConstant.SESSION_USERNAME_KEY, username);
return true;
}
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return false;
}
private boolean checkAuthority(HttpServletRequest request, AccountRoleEnum accountRoleEnum) {
String uri = request.getRequestURI();
if (uri.contains(ApiPrefix.API_V1_NORMAL_PREFIX)) {
// normal 接口都可以访问
return true;
}
if (uri.contains(ApiPrefix.API_V1_RD_PREFIX) ) {
// RD 接口 OP 或者 RD 可以访问
return AccountRoleEnum.RD.equals(accountRoleEnum) || AccountRoleEnum.OP.equals(accountRoleEnum);
}
if (uri.contains(ApiPrefix.API_V1_OP_PREFIX)) {
// OP 接口只有 OP 可以访问
return AccountRoleEnum.OP.equals(accountRoleEnum);
}
return true;
}
}