6.4.2.1. 引入类库

http://www.ioplex.com 下载这个库,然后把这个 JAR 上传到 build.gradle 脚本中注册的一个仓库中。仓库可以是 mavenLocal() 或者内部仓库(私仓)。

build.gradle 中的 web 模块配置部分添加以下依赖:

  1. configure(webModule) {
  2. ...
  3. dependencies {
  4. compile('com.company.thirdparty:jespa:1.1.17') // from a custom repository
  5. compile('jcifs:jcifs:1.3.17') // from Maven Central
  6. ...

web 模块创建一个 LoginProvider 的实现类:

  1. package com.company.sample.web;
  2. import com.company.sample.config.ActiveDirectoryConfig;
  3. import com.company.sample.web.sys.DomainAliasesResolver;
  4. import com.google.common.collect.ImmutableMap;
  5. import com.haulmont.cuba.core.global.ClientType;
  6. import com.haulmont.cuba.core.global.GlobalConfig;
  7. import com.haulmont.cuba.core.sys.AppContext;
  8. import com.haulmont.cuba.core.sys.ConditionalOnAppProperty;
  9. import com.haulmont.cuba.security.auth.*;
  10. import com.haulmont.cuba.security.global.LoginException;
  11. import com.haulmont.cuba.web.auth.WebAuthConfig;
  12. import com.haulmont.cuba.web.security.LoginProvider;
  13. import jespa.http.HttpSecurityService;
  14. import jespa.ntlm.NtlmSecurityProvider;
  15. import jespa.security.PasswordCredential;
  16. import jespa.security.SecurityProviderException;
  17. import org.apache.commons.lang3.StringUtils;
  18. import org.slf4j.Logger;
  19. import org.slf4j.LoggerFactory;
  20. import org.springframework.core.Ordered;
  21. import org.springframework.stereotype.Component;
  22. import javax.annotation.Nullable;
  23. import javax.annotation.PostConstruct;
  24. import javax.inject.Inject;
  25. import javax.servlet.FilterChain;
  26. import javax.servlet.ServletException;
  27. import javax.servlet.ServletRequest;
  28. import javax.servlet.ServletResponse;
  29. import javax.servlet.http.HttpServletRequest;
  30. import java.io.IOException;
  31. import java.io.Serializable;
  32. import java.util.HashMap;
  33. import java.util.Map;
  34. import static com.haulmont.cuba.web.security.ExternalUserCredentials.EXTERNAL_AUTH_USER_SESSION_ATTRIBUTE;
  35. @ConditionalOnAppProperty(property = "activeDirectory.integrationEnabled", value = "true")
  36. @Component("sample_JespaAuthProvider")
  37. public class JespaAuthProvider extends HttpSecurityService implements LoginProvider, Ordered {
  38. private static final Logger log = LoggerFactory.getLogger(JespaAuthProvider.class);
  39. @Inject
  40. private GlobalConfig globalConfig;
  41. @Inject
  42. private WebAuthConfig webAuthConfig;
  43. @Inject
  44. private DomainAliasesResolver domainAliasesResolver;
  45. @Inject
  46. private AuthenticationService authenticationService;
  47. private static Map<String, DomainInfo> domains = new HashMap<>();
  48. private static String defaultDomain;
  49. @PostConstruct
  50. public void init() throws ServletException {
  51. initDomains();
  52. Map<String, String> properties = new HashMap<>();
  53. properties.put("jespa.bindstr", getBindStr());
  54. properties.put("jespa.service.acctname", getAcctName());
  55. properties.put("jespa.service.password", getAcctPassword());
  56. properties.put("jespa.account.canonicalForm", "3");
  57. properties.put("jespa.log.path", globalConfig.getLogDir() + "/jespa.log");
  58. properties.put("http.parameter.anonymous.name", "anon");
  59. fillFromSystemProperties(properties);
  60. try {
  61. super.init(JespaAuthProvider.class.getName(), null, properties);
  62. } catch (SecurityProviderException e) {
  63. throw new ServletException(e);
  64. }
  65. }
  66. @Nullable
  67. @Override
  68. public AuthenticationDetails login(Credentials credentials) throws LoginException {
  69. LoginPasswordCredentials lpCredentials = (LoginPasswordCredentials) credentials;
  70. String login = lpCredentials.getLogin();
  71. // parse domain by login
  72. String domain;
  73. int atSignPos = login.indexOf("@");
  74. if (atSignPos >= 0) {
  75. String domainAlias = login.substring(atSignPos + 1);
  76. domain = domainAliasesResolver.getDomainName(domainAlias).toUpperCase();
  77. } else {
  78. int slashPos = login.indexOf('\\');
  79. if (slashPos <= 0) {
  80. throw new LoginException("Invalid name: %s", login);
  81. }
  82. String domainAlias = login.substring(0, slashPos);
  83. domain = domainAliasesResolver.getDomainName(domainAlias).toUpperCase();
  84. }
  85. DomainInfo domainInfo = domains.get(domain);
  86. if (domainInfo == null) {
  87. throw new LoginException("Unknown domain: %s", domain);
  88. }
  89. Map<String, String> securityProviderProps = new HashMap<>();
  90. securityProviderProps.put("bindstr", domainInfo.getBindStr());
  91. securityProviderProps.put("service.acctname", domainInfo.getAcctName());
  92. securityProviderProps.put("service.password", domainInfo.getAcctPassword());
  93. securityProviderProps.put("account.canonicalForm", "3");
  94. fillFromSystemProperties(securityProviderProps);
  95. NtlmSecurityProvider provider = new NtlmSecurityProvider(securityProviderProps);
  96. try {
  97. PasswordCredential credential = new PasswordCredential(login, lpCredentials.getPassword().toCharArray());
  98. provider.authenticate(credential);
  99. } catch (SecurityProviderException e) {
  100. throw new LoginException("Authentication error: %s", e.getMessage());
  101. }
  102. TrustedClientCredentials trustedCredentials = new TrustedClientCredentials(
  103. lpCredentials.getLogin(),
  104. webAuthConfig.getTrustedClientPassword(),
  105. lpCredentials.getLocale(),
  106. lpCredentials.getParams());
  107. trustedCredentials.setClientInfo(lpCredentials.getClientInfo());
  108. trustedCredentials.setClientType(ClientType.WEB);
  109. trustedCredentials.setIpAddress(lpCredentials.getIpAddress());
  110. trustedCredentials.setOverrideLocale(lpCredentials.isOverrideLocale());
  111. trustedCredentials.setSyncNewUserSessionReplication(lpCredentials.isSyncNewUserSessionReplication());
  112. Map<String, Serializable> targetSessionAttributes;
  113. Map<String, Serializable> sessionAttributes = lpCredentials.getSessionAttributes();
  114. if (sessionAttributes != null
  115. && !sessionAttributes.isEmpty()) {
  116. targetSessionAttributes = new HashMap<>(sessionAttributes);
  117. targetSessionAttributes.put(EXTERNAL_AUTH_USER_SESSION_ATTRIBUTE, true);
  118. } else {
  119. targetSessionAttributes = ImmutableMap.of(EXTERNAL_AUTH_USER_SESSION_ATTRIBUTE, true);
  120. }
  121. trustedCredentials.setSessionAttributes(targetSessionAttributes);
  122. return authenticationService.login(trustedCredentials);
  123. }
  124. @Override
  125. public boolean supports(Class<?> credentialsClass) {
  126. return LoginPasswordCredentials.class.isAssignableFrom(credentialsClass);
  127. }
  128. @Override
  129. public int getOrder() {
  130. return HIGHEST_PLATFORM_PRECEDENCE + 50;
  131. }
  132. @Override
  133. public void destroy() {
  134. }
  135. @Override
  136. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  137. throws IOException, ServletException {
  138. HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  139. if (httpServletRequest.getHeader("User-Agent") != null) {
  140. String ua = httpServletRequest.getHeader("User-Agent")
  141. .toLowerCase();
  142. boolean windows = ua.contains("windows");
  143. boolean gecko = ua.contains("gecko") && !ua.contains("webkit");
  144. if (!windows && gecko) {
  145. chain.doFilter(request, response);
  146. return;
  147. }
  148. }
  149. super.doFilter(request, response, chain);
  150. }
  151. private void initDomains() {
  152. String domainsStr = AppContext.getProperty("activeDirectory.domains");
  153. if (StringUtils.isEmpty(domainsStr)) {
  154. return;
  155. }
  156. String[] strings = domainsStr.split(";");
  157. for (int i = 0; i < strings.length; i++) {
  158. String domain = strings[i];
  159. domain = domain.trim();
  160. if (StringUtils.isEmpty(domain)) {
  161. continue;
  162. }
  163. String[] parts = domain.split("\\|");
  164. if (parts.length != 4) {
  165. log.error("Invalid ActiveDirectory domain definition: " + domain);
  166. break;
  167. } else {
  168. domains.put(parts[0], new DomainInfo(parts[1], parts[2], parts[3]));
  169. if (i == 0) {
  170. defaultDomain = parts[0];
  171. }
  172. }
  173. }
  174. }
  175. public String getDefaultDomain() {
  176. return defaultDomain != null ? defaultDomain : "";
  177. }
  178. public String getBindStr() {
  179. return getBindStr(getDefaultDomain());
  180. }
  181. public String getBindStr(String domain) {
  182. initDomains();
  183. DomainInfo domainInfo = domains.get(domain);
  184. return domainInfo != null ? domainInfo.getBindStr() : "";
  185. }
  186. public String getAcctName() {
  187. return getAcctName(getDefaultDomain());
  188. }
  189. public String getAcctName(String domain) {
  190. initDomains();
  191. DomainInfo domainInfo = domains.get(domain);
  192. return domainInfo != null ? domainInfo.getAcctName() : "";
  193. }
  194. public String getAcctPassword() {
  195. return getAcctPassword(getDefaultDomain());
  196. }
  197. public String getAcctPassword(String domain) {
  198. initDomains();
  199. DomainInfo domainInfo = domains.get(domain);
  200. return domainInfo != null ? domainInfo.getAcctPassword() : "";
  201. }
  202. public void fillFromSystemProperties(Map<String, String> params) {
  203. for (String name : AppContext.getPropertyNames()) {
  204. if (name.startsWith("jespa.")) {
  205. params.put(name, AppContext.getProperty(name));
  206. }
  207. }
  208. }
  209. public static class DomainInfo {
  210. private final String bindStr;
  211. private final String acctName;
  212. private final String acctPassword;
  213. DomainInfo(String bindStr, String acctName, String acctPassword) {
  214. this.acctName = acctName;
  215. this.acctPassword = acctPassword;
  216. this.bindStr = bindStr;
  217. }
  218. public String getBindStr() {
  219. return bindStr;
  220. }
  221. public String getAcctName() {
  222. return acctName;
  223. }
  224. public String getAcctPassword() {
  225. return acctPassword;
  226. }
  227. }
  228. }

创建一个 bean 用来在 web 模块使用别名解析域名:

  1. package com.company.sample.web;
  2. import com.haulmont.cuba.core.sys.AppContext;
  3. import org.apache.commons.lang3.StringUtils;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.stereotype.Component;
  7. import java.util.Arrays;
  8. import java.util.HashMap;
  9. import java.util.List;
  10. import java.util.Map;
  11. import java.util.stream.Collectors;
  12. @Component(DomainAliasesResolver.NAME)
  13. public class DomainAliasesResolver {
  14. public static final String NAME = "sample_DomainAliasesResolver";
  15. private static final Logger log = LoggerFactory.getLogger(DomainAliasesResolver.class);
  16. private Map<String, String> aliases = new HashMap<>();
  17. public DomainAliasesResolver() {
  18. String domainAliases = AppContext.getProperty("activeDirectory.aliases");
  19. if (StringUtils.isEmpty(domainAliases)) {
  20. return;
  21. }
  22. List<String> aliasesPairs = Arrays.stream(StringUtils.split(domainAliases, ';'))
  23. .filter(StringUtils::isNotEmpty)
  24. .collect(Collectors.toList());
  25. for (String aliasDefinition : aliasesPairs) {
  26. String[] aliasParts = StringUtils.split(aliasDefinition, '|');
  27. if (aliasParts == null
  28. || aliasParts.length != 2
  29. || StringUtils.isBlank(aliasParts[0])
  30. || StringUtils.isBlank(aliasParts[1])) {
  31. log.warn("Incorrect domain alias definition: '{}'", aliasDefinition);
  32. } else {
  33. aliases.put(aliasParts[0].toLowerCase(), aliasParts[1]);
  34. }
  35. }
  36. }
  37. public String getDomainName(String alias) {
  38. String alias_lc = alias.toLowerCase();
  39. String domain = aliases.get(alias_lc);
  40. if (domain == null) {
  41. return alias;
  42. }
  43. log.debug("Resolved domain '{}' from alias '{}'", domain, alias);
  44. return domain;
  45. }
  46. }