Commit 2311025b by lanmw

飞机维修厂现场集成AD域单点登录

parent 5b71f719
...@@ -21,6 +21,12 @@ ...@@ -21,6 +21,12 @@
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.0.2</version>
</dependency>
<dependency> <dependency>
<groupId>com.keymobile.authservice</groupId> <groupId>com.keymobile.authservice</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
......
...@@ -4,10 +4,11 @@ import com.keymobile.authservice.component.SecurityConfig; ...@@ -4,10 +4,11 @@ import com.keymobile.authservice.component.SecurityConfig;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySource;
@EnableFeignClients
@SpringBootApplication @SpringBootApplication
@EnableDiscoveryClient @EnableDiscoveryClient
@ComponentScan(basePackages = {"com.keymobile.sso", @ComponentScan(basePackages = {"com.keymobile.sso",
......
package com.keymobile.sso.api;
import com.keymobile.sso.persistence.model.LdapInfo;
import com.keymobile.sso.persistence.model.LdapWhiteList;
import com.keymobile.sso.service.ADService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import java.util.*;
@Tag(name = "AD域相关", description = "AD域相关")
@RestController
@RequestMapping(value = "/adApi")
public class ADApi {
private static final Logger logger = LoggerFactory.getLogger(ADApi.class);
private static final String DEFAULT_TIME_OUT = "5000";
@Autowired
private ADService adService;
@Operation(summary = "保存ldap基本信息")
@PostMapping(value = "/saveLdapInfo")
public LdapInfo saveLdapInfo(@RequestBody LdapInfo ldapInfo) {
return adService.saveLdapInfo(ldapInfo);
}
@Operation(summary = "获取ldap基本信息")
@GetMapping(value = "/getLdapInfo")
public LdapInfo getLdapInfo() {
return adService.getLdapInfo();
}
@Operation(summary = "删除ldap基本信息")
@DeleteMapping(value = "/deleteLdapInfo")
public void deleteLdapInfo() {
adService.deleteLdapInfo();
}
@Operation(summary = "ad域账号登录", description = "ad域账号登录")
@PostMapping(value = "/login")
public String login(HttpServletRequest request,
@RequestParam(value = "username") String username,
@RequestParam(value = "password") String password) {
return adService.login(request, username, password);
}
@Operation(summary = "同步ad域账号")
@PostMapping(value = "/synUser")
public void synUser() {
adService.syncUser();
}
@Operation(summary = "测试ad账号连接", description = "测试ad账号连接")
@PostMapping(value = "/connect")
public void connect(@RequestParam(value = "host") String host,
@RequestParam(value = "port") String port,
@RequestParam(value = "username") String username,
@RequestParam(value = "password") String password) {
logger.info("connect start. host:{},port:{},username:{},password:{}", host, port, username, password);
try {
LdapContext ctx = null;
Hashtable<String, String> hashEnv = new Hashtable<>();
//AD的用户名
hashEnv.put(Context.SECURITY_PRINCIPAL, username);
//AD的密码
hashEnv.put(Context.SECURITY_CREDENTIALS, password);
// LDAP工厂类
hashEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
hashEnv.put("com.sun.jndi.ldap.connect.timeout", DEFAULT_TIME_OUT);
// 默认端口389
hashEnv.put(Context.PROVIDER_URL, "ldap://" + host + ":" + port);
try {
// 初始化上下文
ctx = new InitialLdapContext(hashEnv, null);
logger.info("{}身份验证成功!", username);
} catch (AuthenticationException e) {
logger.error("身份验证失败!", e);
} catch (javax.naming.CommunicationException e) {
logger.error("AD域连接失败!", e);
} catch (Exception e) {
logger.error("身份验证未知异常!", e);
} finally {
if (null != ctx) {
try {
ctx.close();
} catch (Exception e) {
logger.error("上下文关闭错误!", e);
}
}
}
} catch (Exception e) {
logger.error("未知异常!", e);
}
}
@Operation(summary = "连接查询用户", description = "连接查询用户")
@PostMapping(value = "/connectAndSearchUser")
public Object connectAndSearchUser(@RequestParam(value = "host") String host,
@RequestParam(value = "port") String port,
@RequestParam(value = "username") String username,
@RequestParam(value = "dn") String dn,
@RequestParam(value = "password") String password,
@RequestParam(value = "searchuser") String searchuser) {
logger.info("connect start. host:{},port:{},username:{},password:{},dn:{}", host, port, username, password, dn);
List<Map<String, String>> users = new ArrayList<>();
try {
String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
String providerUrl = "ldap://" + host + ":" + port;
String filterPrefix = "(&(objectCategory=Person)(sAMAccountName=";
String filterSuffix = "))";
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
env.put(Context.PROVIDER_URL, providerUrl);
env.put(Context.SECURITY_PRINCIPAL, username);
env.put(Context.SECURITY_CREDENTIALS, password);
LdapContext context = new InitialLdapContext(env, null);
logger.info("{}身份验证成功!", username);
//baseName 可以通过Softerra LDAP Browser查看
//AD-指定搜索范围为个人,并且用户名为客户端输入的用户名称
String filter = filterPrefix + searchuser + filterSuffix;
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
//AD-返回用户的名称、别名及部门
controls.setReturningAttributes(new String[]{"sAMAccountName", "cn", "department"});
NamingEnumeration<SearchResult> answer = context.search(dn, filter, controls);
while (answer.hasMore()) {
SearchResult result = answer.next();
Map<String, String> user = new HashMap<>();
Attributes attrs = result.getAttributes();
Attribute attr = attrs.get("sAMAccountName");
String name = attr == null || attr.get() == null ? null : attr.get().toString();
user.put("sAMAccountName", name);
attr = attrs.get("cn");
String alias = attr == null || attr.get() == null ? null : attr.get().toString();
user.put("cn", alias);
attr = attrs.get("department");
String dept = attr == null || attr.get() == null ? null : attr.get().toString();
user.put("department", dept);
users.add(user);
}
} catch (Exception e) {
logger.error("查询用户报错!", e);
}
return users;
}
@Operation(summary = "连接查询用户", description = "连接查询用户")
@PostMapping(value = "/searchAllUser")
public void searchAllUser(@RequestParam(value = "host") String host,
@RequestParam(value = "port") String port,
@RequestParam(value = "dn") String dn,
@RequestParam(value = "username") String username,
@RequestParam(value = "password") String password) {
logger.info("searchAllUser start. host:{},port:{},username:{},password:{},dn:{}", host, port, username, password, dn);
try {
Hashtable<String, String> hashEnv = new Hashtable<>();
String LDAP_URL = "ldap://" + host + ":" + port;
hashEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
hashEnv.put(Context.PROVIDER_URL, LDAP_URL);
hashEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
hashEnv.put(Context.SECURITY_PRINCIPAL, username);
hashEnv.put(Context.SECURITY_CREDENTIALS, password);
logger.info("env setting");
LdapContext ctx = null;
try {
// 初始化上下文
ctx = new InitialLdapContext(hashEnv, null);
logger.info("{}身份验证成功!", username);
SearchControls searchControls = new SearchControls();
searchControls.setReturningAttributes(null);
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String userSearchFilter = "(&(objectCategory=person)(objectClass=user))";
NamingEnumeration<SearchResult> answer = ctx.search(dn, userSearchFilter, searchControls);
List<Map<String, Object>> lm = new ArrayList<>();
while (answer.hasMore()) {
SearchResult result = answer.next();
NamingEnumeration<? extends Attribute> attrs = result.getAttributes().getAll();
Map<String, Object> map = new HashMap<>();
while (attrs.hasMore()) {
Attribute attr = attrs.next();
map.put(attr.getID(), attr.get());
lm.add(map);
}
}
logger.info("获取到的用户属性为:" + lm);
} catch (AuthenticationException e) {
logger.error("身份验证失败!", e);
} catch (javax.naming.CommunicationException e) {
logger.error("AD域连接失败!", e);
} catch (Exception e) {
logger.error("身份验证未知异常!", e);
} finally {
if (null != ctx) {
try {
ctx.close();
} catch (Exception e) {
logger.error("上下文关闭错误!", e);
}
}
}
} catch (Exception e) {
logger.error("未知异常!", e);
}
}
@Operation(summary = "保存ldap白名单")
@PostMapping(value = "/saveWhiteList")
public LdapWhiteList saveWhiteList(@RequestBody LdapWhiteList whiteList) {
return adService.saveWhiteList(whiteList);
}
@Operation(summary = "删除ldap白名单")
@DeleteMapping(value = "/deleteWhiteList")
public void deleteWhiteList(@RequestParam(required = false) String username) {
adService.deleteWhiteList(username);
}
@Operation(summary = "获取ldap白名单")
@GetMapping(value = "/listWhiteList")
public List<LdapWhiteList> listWhiteList() {
return adService.listWhiteList();
}
}
package com.keymobile.sso.conf;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenAPIConfig {
@Bean
public OpenAPI openAPI() {
Info info = new Info()
.title("sso API文档")
.description("sso API文档");
return new OpenAPI().info(info);
}
}
...@@ -14,7 +14,7 @@ public class RESTAuthenticationEntryPoint implements AuthenticationEntryPoint { ...@@ -14,7 +14,7 @@ public class RESTAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override @Override
public void commence(HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, AuthenticationException authException) public void commence(HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, AuthenticationException authException)
throws IOException { throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED); response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
} }
} }
...@@ -38,6 +38,7 @@ public class SsoSecurityConfig { ...@@ -38,6 +38,7 @@ public class SsoSecurityConfig {
@Bean @Bean
protected SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { protected SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((request) -> { http.authorizeHttpRequests((request) -> {
request.requestMatchers("/adApi/**").permitAll();
request.anyRequest().authenticated(); request.anyRequest().authenticated();
}); });
http.csrf((httpSecurityCsrfConfigurer) -> { http.csrf((httpSecurityCsrfConfigurer) -> {
......
package com.keymobile.sso.exception;
/**
* @author xiesh
* @version 1.0.0
* @date 2024/11/21
* @desc
*/
public class LdapException extends Exception{
private static final long serialVersionUID = 1L;
public LdapException(String errorMsg) {
super(errorMsg);
}
public LdapException(String errorMsg, Throwable cause) {
super(errorMsg, cause);
}
}
package com.keymobile.sso.persistence;
import com.keymobile.sso.persistence.model.LdapInfo;
import jakarta.transaction.Transactional;
import org.springframework.data.repository.CrudRepository;
@Transactional
public interface LdapInfoRepository extends CrudRepository<LdapInfo, String> {
}
package com.keymobile.sso.persistence;
import com.keymobile.sso.persistence.model.LdapWhiteList;
import jakarta.transaction.Transactional;
import org.springframework.data.repository.CrudRepository;
@Transactional
public interface LdapWhiteListRepository extends CrudRepository<LdapWhiteList, String> {
}
package com.keymobile.sso.persistence.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
/**
* @author xiesh
* @version 1.0.0
* @date 2024/4/26
* @desc
*/
@Entity
@Table(name = "sso_ldap_info")
public class LdapInfo {
@Id
private String id;
@Column(name = "HOST", nullable = false)
private String host;
@Column(name = "PORT", nullable = false)
private String port;
@Column(name = "USER_NAME", nullable = false)
private String username;
@Column(name = "PASSWORD", nullable = false)
private String password;
@Column(name = "DN", nullable = false)
private String dn;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDn() {
return dn;
}
public void setDn(String dn) {
this.dn = dn;
}
}
package com.keymobile.sso.persistence.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
/**
* @author xiesh
* @version 1.0.0
* @date 2024/4/26
* @desc
*/
@Entity
@Table(name = "sso_ldap_white_list")
public class LdapWhiteList {
@Id
@Column(name = "USER_NAME", nullable = false)
private String username;
@Column(name = "DNAME")
private String dname;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
package com.keymobile.sso.service;
import com.keymobile.sso.persistence.model.LdapInfo;
import com.keymobile.sso.persistence.model.LdapWhiteList;
import jakarta.servlet.http.HttpServletRequest;
import java.util.List;
/**
* @author xiesh
* @version 1.0.0
* @date 2024/11/20
* @desc ad域服务
*/
public interface ADService {
LdapInfo saveLdapInfo(LdapInfo ldapInfo);
LdapInfo getLdapInfo();
void deleteLdapInfo();
String ldapAuthentication(String username, String password);
String login(HttpServletRequest request, String username, String password);
LdapWhiteList saveWhiteList(LdapWhiteList whiteList);
void deleteWhiteList(String username);
List<LdapWhiteList> listWhiteList();
void syncUser();
}
package com.keymobile.sso.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@FeignClient(value = "authService")
public interface AuthRemoteService {
@RequestMapping(value = "/users/findByName")
List<Map<String, Object>> getUserByName(@RequestParam(value = "match") String match);
@PostMapping(value = "/users")
Map<String, Object> addUser(@RequestBody Map<String, Object> user);
@PostMapping(value = "/users/{userId}")
Map<String, Object> updateUser(@PathVariable(value = "userId") Long userId, @RequestBody Map<String, Object> user);
}
package com.keymobile.sso.service.impl;
import com.keymobile.authservice.component.CustomizedUserDetailService;
import com.keymobile.sso.exception.LdapException;
import com.keymobile.sso.logging.LogManager;
import com.keymobile.sso.persistence.LdapInfoRepository;
import com.keymobile.sso.persistence.LdapWhiteListRepository;
import com.keymobile.sso.persistence.model.LdapInfo;
import com.keymobile.sso.persistence.model.LdapWhiteList;
import com.keymobile.sso.service.ADService;
import com.keymobile.sso.service.AuthRemoteService;
import com.keymobile.sso.util.AES;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class ADServiceImpl implements ADService {
@Autowired
private LdapInfoRepository ldapInfoRepository;
@Autowired
private LdapWhiteListRepository ldapWhiteListRepository;
private static final Logger logger = LoggerFactory.getLogger(ADServiceImpl.class);
private static final String DEFAULT_TIME_OUT = "5000";
private static final String CTX_API = "sso.API";
@Autowired
private AuthRemoteService authService;
@Autowired
private CustomizedUserDetailService customizedUserDetailService;
public static final String LDAP_ID = "ldap";
public static final String LADP_S_AMACCOUNT_NAME = "sAMAccountName";
public static final String LADP_DISTINGUISHED_NAME = "distinguishedName";
public static final String LADP_CN = "cn";
@Value("${ad.defaultRoleId:2}")
private Long defaultRoleId;
@Value("${ad.limit:true}")
private String adLimit;
// ====================== 改造开始:多节点AD连接 ======================
/**
* 多节点LDAP连接(自动主从切换)
*/
/**
* 支持多节点、每个节点可独立配置端口
* 格式示例:
* 同端口:10.240.16.101,10.240.16.102
* 不同端口:10.240.16.101:389,10.240.16.102:636
*/
private LdapContext connectToLdap(String bindUser, String bindPwd) {
LdapInfo ldapInfo = getLdapInfo();
if (ldapInfo == null) {
logger.error("未配置LDAP信息");
return null;
}
// 拆分节点列表
List<String> nodeList = Arrays.stream(ldapInfo.getHost().split(","))
.map(String::trim)
.filter(StringUtils::isNotEmpty)
.toList();
if (nodeList.isEmpty()) {
logger.error("LDAP主机配置为空");
return null;
}
// 全局默认端口
String globalPort = ldapInfo.getPort();
String dn = ldapInfo.getDn();
for (String node : nodeList) {
String host;
String port;
// 解析 节点自带端口 或 使用全局端口
if (node.contains(":")) {
String[] hp = node.split(":", 2);
host = hp[0].trim();
port = hp[1].trim();
} else {
host = node;
port = globalPort;
}
Hashtable<String, String> env = new Hashtable<>();
String ldapUrl = "ldap://" + host + ":" + port;
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapUrl);
env.put(Context.SECURITY_PRINCIPAL, bindUser);
env.put(Context.SECURITY_CREDENTIALS, bindPwd);
env.put("com.sun.jndi.ldap.connect.timeout", DEFAULT_TIME_OUT);
env.put("com.sun.jndi.ldap.read.timeout", "5000");
try {
logger.info("尝试连接AD域节点:{} -> 账号:{}", ldapUrl, bindUser);
LdapContext ctx = new InitialLdapContext(env, null);
logger.info("AD域节点连接成功:{}", ldapUrl);
return ctx;
} catch (Exception e) {
logger.error("AD域节点连接失败:{},原因:{}", ldapUrl, e.getMessage());
}
}
logger.error("所有AD域节点全部连接失败");
return null;
}
// ====================== 改造:登录认证(多节点) ======================
@Override
public String ldapAuthentication(String username, String password) {
LdapInfo ldapInfo = getLdapInfo();
if (ldapInfo == null) return "未配置ldap";
String dn = ldapInfo.getDn();
String ldapUserName = username + "@" + dn;
LdapContext ctx = connectToLdap(ldapUserName, password);
if (ctx != null) {
try {
ctx.close();
} catch (Exception ignored) {}
return "ok";
} else {
return "AD域身份验证失败或所有域控不可用";
}
}
// ====================== 改造:查询用户信息(多节点) ======================
private Map<String, String> searchUserInfoByName(String searchName) throws Exception {
LdapInfo ldapInfo = getLdapInfo();
if (ldapInfo == null) throw new LdapException("未配置ldap信息");
String bindUser = ldapInfo.getUsername() + "@" + ldapInfo.getDn();
String bindPwd = AES.decrypt(ldapInfo.getPassword());
LdapContext ctx = connectToLdap(bindUser, bindPwd);
if (ctx == null) throw new Exception("AD域连接失败,无法查询用户");
Map<String, String> userInfo = new HashMap<>();
try {
String[] dnArg = StringUtils.split(ldapInfo.getDn(), ".");
String dnStr = Arrays.stream(dnArg)
.map(e -> "DC=" + e)
.collect(Collectors.joining(","));
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
searchControls.setReturningAttributes(new String[]{LADP_S_AMACCOUNT_NAME, LADP_CN, LADP_DISTINGUISHED_NAME});
String filter = String.format("(&(objectCategory=person)(objectClass=user)(sAMAccountName=%s))", searchName);
NamingEnumeration<SearchResult> answer = ctx.search(dnStr, filter, searchControls);
if (answer.hasMore()) {
SearchResult result = answer.next();
Attributes attrs = result.getAttributes();
Attribute attr = attrs.get(LADP_S_AMACCOUNT_NAME);
userInfo.put(LADP_S_AMACCOUNT_NAME, attr == null ? null : attr.get().toString());
attr = attrs.get(LADP_CN);
userInfo.put(LADP_CN, attr == null ? null : attr.get().toString());
attr = attrs.get(LADP_DISTINGUISHED_NAME);
userInfo.put(LADP_DISTINGUISHED_NAME, attr == null ? null : attr.get().toString());
}
} finally {
try { ctx.close(); } catch (Exception e) {}
}
return userInfo;
}
// ====================== 改造:同步用户(多节点) ======================
@Override
public void syncUser() {
logger.info("ad域账号同步开始");
LdapInfo ldapInfo = getLdapInfo();
if (ldapInfo == null) {
logger.error("未配置ldap");
return;
}
String bindUser = ldapInfo.getUsername() + "@" + ldapInfo.getDn();
String bindPwd = AES.decrypt(ldapInfo.getPassword());
LdapContext ctx = connectToLdap(bindUser, bindPwd);
if (ctx == null) {
logger.error("AD域同步用户失败:所有域控连接失败");
return;
}
try {
String[] dnArg = StringUtils.split(ldapInfo.getDn(), ".");
String dnStr = Arrays.stream(dnArg)
.map(e -> "DC=" + e)
.collect(Collectors.joining(","));
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration<SearchResult> answer = ctx.search(dnStr, "(&(objectCategory=person)(objectClass=user))", controls);
while (answer.hasMore()) {
Map<String, String> ldapUser = new HashMap<>();
Attributes attrs = answer.next().getAttributes();
Attribute attr = attrs.get(LADP_S_AMACCOUNT_NAME);
String name = attr == null ? null : attr.get().toString();
ldapUser.put(LADP_S_AMACCOUNT_NAME, name);
attr = attrs.get(LADP_CN);
String alias = attr == null ? name : attr.get().toString();
ldapUser.put(LADP_CN, alias);
if (StringUtils.isNotBlank(name)) {
Map<String, Object> user = getUserByName(name);
if (user == null) {
Map<String, Object> toAdd = new HashMap<>();
toAdd.put("name", name);
toAdd.put("dname", ldapUser.get(LADP_CN));
toAdd.put("password", "37fa265330ad83eaa879efb12312db6380896cf639");
List<Map<String, Object>> roles = new ArrayList<>();
Map<String, Object> roleMap = new HashMap<>();
roleMap.put("id", defaultRoleId);
roles.add(roleMap);
toAdd.put("dataRoleAbstracts", roles);
authService.addUser(toAdd);
logger.info("同步AD用户:{}", name);
}
}
}
} catch (Exception e) {
logger.error("同步用户异常", e);
} finally {
try { ctx.close(); } catch (Exception e) {}
}
logger.info("ad域账号同步结束");
}
// ====================== 以下代码完全不动,保持你原有逻辑 ======================
@Override
public LdapInfo saveLdapInfo(LdapInfo ldapInfo) {
ldapInfo.setId(LDAP_ID);
LdapInfo oldLdap = getLdapInfo();
if (oldLdap == null || !StringUtils.equals(ldapInfo.getPassword(), oldLdap.getPassword())) {
ldapInfo.setPassword(AES.encrypt(ldapInfo.getPassword()));
}
return ldapInfoRepository.save(ldapInfo);
}
@Override
public LdapInfo getLdapInfo() {
return ldapInfoRepository.findById(LDAP_ID).orElse(null);
}
@Override
public void deleteLdapInfo() {
ldapInfoRepository.deleteAll();
}
@Override
public String login(HttpServletRequest request, String username, String password) {
String result = null;
if (!checkWhiteList(username)) {
return "用户无权访问";
}
try {
result = ldapAuthentication(username, password);
if (StringUtils.equals(result, "ok")) {
Map<String, Object> user = getUserByName(username);
if (null == user || CollectionUtils.isEmpty(user)) {
Map<String, String> ldapUserInfo = searchUserInfoByName(username);
Map<String, Object> toAdd = new HashMap<>();
toAdd.put("name", username);
toAdd.put("dname", ldapUserInfo.get(LADP_CN) == null ? username : ldapUserInfo.get(LADP_CN));
toAdd.put("password", "37fa265330ad83eaa879efb12312db6380896cf639");
List<Map<String, Object>> dataRoleAbstracts = new ArrayList<>();
Map<String, Object> roleMap = new HashMap<>();
roleMap.put("id", defaultRoleId);
dataRoleAbstracts.add(roleMap);
toAdd.put("dataRoleAbstracts", dataRoleAbstracts);
authService.addUser(toAdd);
}
UserDetails userDetails = customizedUserDetailService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
HttpSession session = request.getSession(true);
session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
MDC.put("user", username);
MDC.put("session", session.getId());
LogManager.logInfo(CTX_API, "统一账号认证登录");
}
} catch (Exception e) {
logger.error("登录异常!", e);
result = e.getMessage();
}
return result;
}
@Override
public LdapWhiteList saveWhiteList(LdapWhiteList ldapWhiteList) {
return ldapWhiteListRepository.save(ldapWhiteList);
}
@Override
public void deleteWhiteList(String username) {
if (StringUtils.isNotBlank(username)) {
ldapWhiteListRepository.deleteById(username);
} else {
ldapWhiteListRepository.deleteAll();
}
}
@Override
public List<LdapWhiteList> listWhiteList() {
return (List<LdapWhiteList>) ldapWhiteListRepository.findAll();
}
private Map<String, Object> getUserByName(String username) {
List<Map<String, Object>> matchUser = authService.getUserByName(username);
if (matchUser != null) {
return matchUser.stream()
.filter(e -> StringUtils.equals(username, String.valueOf(e.get("name"))))
.findFirst().orElse(null);
}
return null;
}
private Boolean checkWhiteList(String userName) {
if (!StringUtils.equals("true", adLimit)) {
return true;
}
return ldapWhiteListRepository.findById(userName).isPresent();
}
}
\ No newline at end of file
package com.keymobile.sso.util;
import org.apache.commons.lang.StringUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class AES {
//编码方式
public static final String CODE_TYPE = "UTF-8";
//填充类型
public static final String AES_TYPE = "AES/ECB/PKCS5Padding";
//私钥
private static String AES_KEY = "4444111133332222"; //AES固定格式为128/192/256 bits.即:16/24/32bytes。DES固定格式为128bits,即8bytes。
/**
* 加密
*
* @param cleartext
* @return
*/
public static String encrypt(String cleartext) {
//加密方式: AES128(CBC/PKCS5Padding) + Base64, 私钥:1111222233334444
try {
if(StringUtils.isNotBlank(cleartext)){
//IvParameterSpec zeroIv = new IvParameterSpec(VIPARA.getBytes());
//两个参数,第一个为私钥字节数组, 第二个为加密方式 AES或者DES
SecretKeySpec key = new SecretKeySpec(AES_KEY.getBytes(StandardCharsets.UTF_8), "AES");
//实例化加密类,参数为加密方式,要写全
Cipher cipher = Cipher.getInstance(AES_TYPE); //PKCS5Padding比PKCS7Padding效率高,PKCS7Padding可支持IOS加解密
//初始化,此方法可以采用三种方式,按加密算法要求来添加。(1)无第三个参数(2)第三个参数为SecureRandom random = new SecureRandom();中random对象,随机数。(AES不可采用这种方法)(3)采用此代码中的IVParameterSpec
//加密时使用:ENCRYPT_MODE; 解密时使用:DECRYPT_MODE;
cipher.init(Cipher.ENCRYPT_MODE, key); //CBC类型的可以在第三个参数传递偏移量zeroIv,ECB没有偏移量
//加密操作,返回加密后的字节数组,然后需要编码。主要编解码方式有Base64, HEX, UUE,7bit等等。此处看服务器需要什么编码方式
byte[] encryptedData = cipher.doFinal(cleartext.getBytes(StandardCharsets.UTF_8));
// 修改点:使用 java.util.Base64 替代 sun.misc.BASE64Encoder
return Base64.getEncoder().encodeToString(encryptedData);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 解密
*
* @param encrypted
* @return
*/
public static String decrypt(String encrypted) {
try {
if(StringUtils.isNotBlank(encrypted)){
// 修改点:使用 java.util.Base64 替代 sun.misc.BASE64Decoder
byte[] byteMi = Base64.getDecoder().decode(encrypted);
//IvParameterSpec zeroIv = new IvParameterSpec(VIPARA.getBytes());
SecretKeySpec key = new SecretKeySpec(AES_KEY.getBytes(StandardCharsets.UTF_8), "AES");
Cipher cipher = Cipher.getInstance(AES_TYPE);
//与加密时不同MODE:Cipher.DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, key); //CBC类型的可以在第三个参数传递偏移量zeroIv,ECB没有偏移量
byte[] decryptedData = cipher.doFinal(byteMi);
return new String(decryptedData, CODE_TYPE);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void setAesKey(String aesKey){
AES_KEY = aesKey;
}
/**
* 测试
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
String pass = "Ims1555#";
setAesKey("4444111133332222");
System.out.println("加密内容:"+encrypt(pass));
String content = encrypt(pass);
System.out.println("解密内容:"+decrypt(content));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment