新增LDAP认证登录
This commit is contained in:
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||||
|
*/
|
||||||
|
package com.jeesite.common.shiro.authc;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LdapToken
|
||||||
|
* @author ThinkGem
|
||||||
|
* @version 2021-7-6
|
||||||
|
*/
|
||||||
|
public class LdapToken extends FormToken {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public LdapToken() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LdapToken(String username, char[] password, boolean rememberMe,
|
||||||
|
String host, Map<String, Object> params) {
|
||||||
|
super(username, password, rememberMe, null, host, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||||
|
*/
|
||||||
|
package com.jeesite.common.shiro.filter;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
|
||||||
|
import org.apache.shiro.authc.AuthenticationToken;
|
||||||
|
|
||||||
|
import com.jeesite.common.shiro.authc.LdapToken;
|
||||||
|
import com.jeesite.common.web.http.ServletUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LDAP过滤器
|
||||||
|
* @author ThinkGem
|
||||||
|
* @version 2021-7-6
|
||||||
|
*/
|
||||||
|
public class LdapFilter extends FormFilter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
|
||||||
|
String username = getUsername(request, response); // 用户名
|
||||||
|
String password = getPassword(request); // 登录密码
|
||||||
|
boolean rememberMe = isRememberMe(request); // 记住我(自动登录)
|
||||||
|
String host = getHost(request); // 登录主机
|
||||||
|
Map<String, Object> paramMap = ServletUtils.getExtParams(request); // 登录附加参数
|
||||||
|
return new LdapToken(username, password.toCharArray(), rememberMe, host, paramMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isLoginRequest(ServletRequest request, ServletResponse response) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,317 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||||
|
*/
|
||||||
|
package com.jeesite.common.shiro.realm;
|
||||||
|
|
||||||
|
import javax.naming.AuthenticationNotSupportedException;
|
||||||
|
import javax.naming.NamingException;
|
||||||
|
import javax.naming.ldap.LdapContext;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.apache.shiro.authc.AuthenticationException;
|
||||||
|
import org.apache.shiro.authc.AuthenticationInfo;
|
||||||
|
import org.apache.shiro.authc.AuthenticationToken;
|
||||||
|
import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher;
|
||||||
|
import org.apache.shiro.authz.AuthorizationInfo;
|
||||||
|
import org.apache.shiro.ldap.UnsupportedAuthenticationMechanismException;
|
||||||
|
import org.apache.shiro.realm.ldap.DefaultLdapRealm;
|
||||||
|
import org.apache.shiro.realm.ldap.JndiLdapContextFactory;
|
||||||
|
import org.apache.shiro.realm.ldap.LdapContextFactory;
|
||||||
|
import org.apache.shiro.realm.ldap.LdapUtils;
|
||||||
|
import org.apache.shiro.session.Session;
|
||||||
|
import org.apache.shiro.subject.Subject;
|
||||||
|
import org.apache.shiro.util.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.jeesite.common.shiro.authc.FormToken;
|
||||||
|
import com.jeesite.common.shiro.authc.LdapToken;
|
||||||
|
import com.jeesite.common.utils.SpringUtils;
|
||||||
|
import com.jeesite.common.web.http.ServletUtils;
|
||||||
|
import com.jeesite.modules.sys.entity.Log;
|
||||||
|
import com.jeesite.modules.sys.entity.User;
|
||||||
|
import com.jeesite.modules.sys.service.EmpUserService;
|
||||||
|
import com.jeesite.modules.sys.service.UserService;
|
||||||
|
import com.jeesite.modules.sys.utils.LogUtils;
|
||||||
|
import com.jeesite.modules.sys.utils.UserUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统认证授权实现类
|
||||||
|
* @author ThinkGem
|
||||||
|
* @version 2021-7-6
|
||||||
|
*/
|
||||||
|
public class LdapAuthorizingRealm extends BaseAuthorizingRealm {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(DefaultLdapRealm.class);
|
||||||
|
|
||||||
|
//The zero index currently means nothing, but could be utilized in the future for other substitution techniques.
|
||||||
|
private static final String USERDN_SUBSTITUTION_TOKEN = "{0}";
|
||||||
|
|
||||||
|
private String userDnPrefix;
|
||||||
|
private String userDnSuffix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The LdapContextFactory instance used to acquire {@link javax.naming.ldap.LdapContext LdapContext}'s at runtime
|
||||||
|
* to acquire connections to the LDAP directory to perform authentication attempts and authorizatino queries.
|
||||||
|
*/
|
||||||
|
private LdapContextFactory contextFactory;
|
||||||
|
|
||||||
|
private UserService userService;
|
||||||
|
private EmpUserService empUserService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default no-argument constructor that defaults the internal {@link LdapContextFactory} instance to a
|
||||||
|
* {@link JndiLdapContextFactory}.
|
||||||
|
*/
|
||||||
|
public LdapAuthorizingRealm() {
|
||||||
|
super();
|
||||||
|
//Credentials Matching is not necessary - the LDAP directory will do it automatically:
|
||||||
|
setCredentialsMatcher(new AllowAllCredentialsMatcher());
|
||||||
|
//Any Object principal and Object credentials may be passed to the LDAP provider, so accept any token:
|
||||||
|
setAuthenticationTokenClass(LdapToken.class);
|
||||||
|
this.contextFactory = new JndiLdapContextFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FormToken getFormToken(AuthenticationToken authcToken) {
|
||||||
|
HttpServletRequest request = ServletUtils.getRequest();
|
||||||
|
if (authcToken == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
LdapToken ldapToken = (LdapToken) authcToken;
|
||||||
|
|
||||||
|
// LDAP 身份认证
|
||||||
|
LdapContext ctx = null;
|
||||||
|
try {
|
||||||
|
Object principal = getUserDn(ldapToken.getUsername());
|
||||||
|
Object credentials = String.valueOf(ldapToken.getPassword());
|
||||||
|
log.debug("Authenticating user '{}' through LDAP", principal);
|
||||||
|
ctx = getContextFactory().getLdapContext(principal, credentials);
|
||||||
|
} catch (AuthenticationNotSupportedException e) {
|
||||||
|
throw new UnsupportedAuthenticationMechanismException("msg:LDAP 不支持的授权类型", e);
|
||||||
|
} catch (javax.naming.AuthenticationException e) {
|
||||||
|
throw new AuthenticationException("msg:LDAP 授权失败:"+e.getMessage(), e);
|
||||||
|
} catch (NamingException e) {
|
||||||
|
throw new AuthenticationException("msg:LDAP 连接失败:"+e.getMessage(), e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new AuthenticationException("msg:LDAP 登录失败:"+e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
LdapUtils.closeContext(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成登录信息对象
|
||||||
|
FormToken token = new FormToken(request);
|
||||||
|
token.setUsername(ldapToken.getUsername());
|
||||||
|
token.setPassword(ldapToken.getPassword());
|
||||||
|
token.setParams(ldapToken.getParams());
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected User getUserInfo(FormToken token) {
|
||||||
|
User user = super.getUserInfo(token);
|
||||||
|
if (user == null){
|
||||||
|
throw new AuthenticationException("msg:用户 “" + token.getUsername() + "” 在本系统中不存在, 请联系管理员.");
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assertCredentialsMatch(AuthenticationToken authcToken,
|
||||||
|
AuthenticationInfo info) throws AuthenticationException {
|
||||||
|
// 已经在 getFormToken 认证过了,这里就不验证身份了
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AuthorizationInfo doGetAuthorizationInfo(LoginInfo loginInfo, Subject subject, Session session, User user) {
|
||||||
|
return super.doGetAuthorizationInfo(loginInfo, subject, session, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoginSuccess(LoginInfo loginInfo, HttpServletRequest request) {
|
||||||
|
super.onLoginSuccess(loginInfo, request);
|
||||||
|
|
||||||
|
//System.out.print("__sid: "+request.getSession().getId());
|
||||||
|
//System.out.println(" == "+UserUtils.getSession().getId());
|
||||||
|
|
||||||
|
// 更新登录IP、时间、会话ID等
|
||||||
|
User user = UserUtils.get(loginInfo.getId());
|
||||||
|
getUserService().updateUserLoginInfo(user);
|
||||||
|
|
||||||
|
// 记录用户登录日志
|
||||||
|
LogUtils.saveLog(user, ServletUtils.getRequest(), "系统登录", Log.TYPE_LOGIN_LOGOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLogoutSuccess(LoginInfo loginInfo, HttpServletRequest request) {
|
||||||
|
super.onLogoutSuccess(loginInfo, request);
|
||||||
|
|
||||||
|
// 记录用户退出日志
|
||||||
|
User user = UserUtils.get(loginInfo.getId());
|
||||||
|
LogUtils.saveLog(user, request, "系统退出", Log.TYPE_LOGIN_LOGOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserService getUserService() {
|
||||||
|
if (userService == null){
|
||||||
|
userService = SpringUtils.getBean(UserService.class);
|
||||||
|
}
|
||||||
|
return userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmpUserService getEmpUserService() {
|
||||||
|
if (empUserService == null){
|
||||||
|
empUserService = SpringUtils.getBean(EmpUserService.class);
|
||||||
|
}
|
||||||
|
return empUserService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the User DN prefix to use when building a runtime User DN value or {@code null} if no
|
||||||
|
* {@link #getUserDnTemplate() userDnTemplate} has been configured. If configured, this value is the text that
|
||||||
|
* occurs before the {@link #USERDN_SUBSTITUTION_TOKEN} in the {@link #getUserDnTemplate() userDnTemplate} value.
|
||||||
|
*
|
||||||
|
* @return the the User DN prefix to use when building a runtime User DN value or {@code null} if no
|
||||||
|
* {@link #getUserDnTemplate() userDnTemplate} has been configured.
|
||||||
|
*/
|
||||||
|
protected String getUserDnPrefix() {
|
||||||
|
return userDnPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the User DN suffix to use when building a runtime User DN value. or {@code null} if no
|
||||||
|
* {@link #getUserDnTemplate() userDnTemplate} has been configured. If configured, this value is the text that
|
||||||
|
* occurs after the {@link #USERDN_SUBSTITUTION_TOKEN} in the {@link #getUserDnTemplate() userDnTemplate} value.
|
||||||
|
*
|
||||||
|
* @return the User DN suffix to use when building a runtime User DN value or {@code null} if no
|
||||||
|
* {@link #getUserDnTemplate() userDnTemplate} has been configured.
|
||||||
|
*/
|
||||||
|
protected String getUserDnSuffix() {
|
||||||
|
return userDnSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the User Distinguished Name (DN) template to use when creating User DNs at runtime. A User DN is an LDAP
|
||||||
|
* fully-qualified unique user identifier which is required to establish a connection with the LDAP
|
||||||
|
* directory to authenticate users and query for authorization information.
|
||||||
|
* <h2>Usage</h2>
|
||||||
|
* User DN formats are unique to the LDAP directory's schema, and each environment differs - you will need to
|
||||||
|
* specify the format corresponding to your directory. You do this by specifying the full User DN as normal, but
|
||||||
|
* but you use a <b>{@code {0}}</b> placeholder token in the string representing the location where the
|
||||||
|
* user's submitted principal (usually a username or uid) will be substituted at runtime.
|
||||||
|
* <p/>
|
||||||
|
* For example, if your directory
|
||||||
|
* uses an LDAP {@code uid} attribute to represent usernames, the User DN for the {@code jsmith} user may look like
|
||||||
|
* this:
|
||||||
|
* <p/>
|
||||||
|
* <pre>uid=jsmith,ou=users,dc=mycompany,dc=com</pre>
|
||||||
|
* <p/>
|
||||||
|
* in which case you would set this property with the following template value:
|
||||||
|
* <p/>
|
||||||
|
* <pre>uid=<b>{0}</b>,ou=users,dc=mycompany,dc=com</pre>
|
||||||
|
* <p/>
|
||||||
|
* If no template is configured, the raw {@code AuthenticationToken}
|
||||||
|
* {@link AuthenticationToken#getPrincipal() principal} will be used as the LDAP principal. This is likely
|
||||||
|
* incorrect as most LDAP directories expect a fully-qualified User DN as opposed to the raw uid or username. So,
|
||||||
|
* ensure you set this property to match your environment!
|
||||||
|
*
|
||||||
|
* @param template the User Distinguished Name template to use for runtime substitution
|
||||||
|
* @throws IllegalArgumentException if the template is null, empty, or does not contain the
|
||||||
|
* {@code {0}} substitution token.
|
||||||
|
* @see LdapContextFactory#getLdapContext(Object,Object)
|
||||||
|
*/
|
||||||
|
public void setUserDnTemplate(String template) throws IllegalArgumentException {
|
||||||
|
if (!StringUtils.hasText(template)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int index = template.indexOf(USERDN_SUBSTITUTION_TOKEN);
|
||||||
|
if (index < 0) {
|
||||||
|
String msg = "User DN template must contain the '" +
|
||||||
|
USERDN_SUBSTITUTION_TOKEN + "' replacement token to understand where to " +
|
||||||
|
"insert the runtime authentication principal.";
|
||||||
|
throw new IllegalArgumentException(msg);
|
||||||
|
}
|
||||||
|
String prefix = template.substring(0, index);
|
||||||
|
String suffix = template.substring(prefix.length() + USERDN_SUBSTITUTION_TOKEN.length());
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Determined user DN prefix [{}] and suffix [{}]", prefix, suffix);
|
||||||
|
}
|
||||||
|
this.userDnPrefix = prefix;
|
||||||
|
this.userDnSuffix = suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the User Distinguished Name (DN) template to use when creating User DNs at runtime - see the
|
||||||
|
* {@link #setUserDnTemplate(String) setUserDnTemplate} JavaDoc for a full explanation.
|
||||||
|
*
|
||||||
|
* @return the User Distinguished Name (DN) template to use when creating User DNs at runtime.
|
||||||
|
*/
|
||||||
|
public String getUserDnTemplate() {
|
||||||
|
return getUserDn(USERDN_SUBSTITUTION_TOKEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the LDAP User Distinguished Name (DN) to use when acquiring an
|
||||||
|
* {@link javax.naming.ldap.LdapContext LdapContext} from the {@link LdapContextFactory}.
|
||||||
|
* <p/>
|
||||||
|
* If the the {@link #getUserDnTemplate() userDnTemplate} property has been set, this implementation will construct
|
||||||
|
* the User DN by substituting the specified {@code principal} into the configured template. If the
|
||||||
|
* {@link #getUserDnTemplate() userDnTemplate} has not been set, the method argument will be returned directly
|
||||||
|
* (indicating that the submitted authentication token principal <em>is</em> the User DN).
|
||||||
|
*
|
||||||
|
* @param principal the principal to substitute into the configured {@link #getUserDnTemplate() userDnTemplate}.
|
||||||
|
* @return the constructed User DN to use at runtime when acquiring an {@link javax.naming.ldap.LdapContext}.
|
||||||
|
* @throws IllegalArgumentException if the method argument is null or empty
|
||||||
|
* @throws IllegalStateException if the {@link #getUserDnTemplate userDnTemplate} has not been set.
|
||||||
|
* @see LdapContextFactory#getLdapContext(Object, Object)
|
||||||
|
*/
|
||||||
|
protected String getUserDn(String principal) throws IllegalArgumentException, IllegalStateException {
|
||||||
|
if (!StringUtils.hasText(principal)) {
|
||||||
|
throw new IllegalArgumentException("User principal cannot be null or empty for User DN construction.");
|
||||||
|
}
|
||||||
|
String prefix = getUserDnPrefix();
|
||||||
|
String suffix = getUserDnSuffix();
|
||||||
|
if (prefix == null && suffix == null) {
|
||||||
|
log.debug("userDnTemplate property has not been configured, indicating the submitted " +
|
||||||
|
"AuthenticationToken's principal is the same as the User DN. Returning the method argument " +
|
||||||
|
"as is.");
|
||||||
|
return principal;
|
||||||
|
}
|
||||||
|
|
||||||
|
int prefixLength = prefix != null ? prefix.length() : 0;
|
||||||
|
int suffixLength = suffix != null ? suffix.length() : 0;
|
||||||
|
StringBuilder sb = new StringBuilder(prefixLength + principal.length() + suffixLength);
|
||||||
|
if (prefixLength > 0) {
|
||||||
|
sb.append(prefix);
|
||||||
|
}
|
||||||
|
sb.append(principal);
|
||||||
|
if (suffixLength > 0) {
|
||||||
|
sb.append(suffix);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the LdapContextFactory instance used to acquire connections to the LDAP directory during authentication
|
||||||
|
* attempts and authorization queries. Unless specified otherwise, the default is a {@link JndiLdapContextFactory}
|
||||||
|
* instance.
|
||||||
|
*
|
||||||
|
* @param contextFactory the LdapContextFactory instance used to acquire connections to the LDAP directory during
|
||||||
|
* authentication attempts and authorization queries
|
||||||
|
*/
|
||||||
|
public void setContextFactory(LdapContextFactory contextFactory) {
|
||||||
|
this.contextFactory = contextFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the LdapContextFactory instance used to acquire connections to the LDAP directory during authentication
|
||||||
|
* attempts and authorization queries. Unless specified otherwise, the default is a {@link JndiLdapContextFactory}
|
||||||
|
* instance.
|
||||||
|
*
|
||||||
|
* @return the LdapContextFactory instance used to acquire connections to the LDAP directory during
|
||||||
|
* authentication attempts and authorization queries
|
||||||
|
*/
|
||||||
|
public LdapContextFactory getContextFactory() {
|
||||||
|
return this.contextFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import javax.servlet.Filter;
|
|||||||
import org.apache.shiro.cache.CacheManager;
|
import org.apache.shiro.cache.CacheManager;
|
||||||
import org.apache.shiro.cas.CasSubjectFactory;
|
import org.apache.shiro.cas.CasSubjectFactory;
|
||||||
import org.apache.shiro.realm.Realm;
|
import org.apache.shiro.realm.Realm;
|
||||||
|
import org.apache.shiro.realm.ldap.JndiLdapContextFactory;
|
||||||
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
|
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
|
||||||
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
|
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
|
||||||
import org.apache.shiro.web.filter.InvalidRequestFilter;
|
import org.apache.shiro.web.filter.InvalidRequestFilter;
|
||||||
@@ -29,12 +30,14 @@ import com.jeesite.common.shiro.config.FilterChainDefinitionMap;
|
|||||||
import com.jeesite.common.shiro.filter.CasFilter;
|
import com.jeesite.common.shiro.filter.CasFilter;
|
||||||
import com.jeesite.common.shiro.filter.FormFilter;
|
import com.jeesite.common.shiro.filter.FormFilter;
|
||||||
import com.jeesite.common.shiro.filter.InnerFilter;
|
import com.jeesite.common.shiro.filter.InnerFilter;
|
||||||
|
import com.jeesite.common.shiro.filter.LdapFilter;
|
||||||
import com.jeesite.common.shiro.filter.LogoutFilter;
|
import com.jeesite.common.shiro.filter.LogoutFilter;
|
||||||
import com.jeesite.common.shiro.filter.PermissionsFilter;
|
import com.jeesite.common.shiro.filter.PermissionsFilter;
|
||||||
import com.jeesite.common.shiro.filter.RolesFilter;
|
import com.jeesite.common.shiro.filter.RolesFilter;
|
||||||
import com.jeesite.common.shiro.filter.UserFilter;
|
import com.jeesite.common.shiro.filter.UserFilter;
|
||||||
import com.jeesite.common.shiro.realm.AuthorizingRealm;
|
import com.jeesite.common.shiro.realm.AuthorizingRealm;
|
||||||
import com.jeesite.common.shiro.realm.CasAuthorizingRealm;
|
import com.jeesite.common.shiro.realm.CasAuthorizingRealm;
|
||||||
|
import com.jeesite.common.shiro.realm.LdapAuthorizingRealm;
|
||||||
import com.jeesite.common.shiro.session.SessionDAO;
|
import com.jeesite.common.shiro.session.SessionDAO;
|
||||||
import com.jeesite.common.shiro.session.SessionManager;
|
import com.jeesite.common.shiro.session.SessionManager;
|
||||||
import com.jeesite.common.shiro.web.ShiroFilterFactoryBean;
|
import com.jeesite.common.shiro.web.ShiroFilterFactoryBean;
|
||||||
@@ -77,6 +80,15 @@ public class ShiroConfig {
|
|||||||
bean.setAuthorizingRealm(casAuthorizingRealm);
|
bean.setAuthorizingRealm(casAuthorizingRealm);
|
||||||
return bean;
|
return bean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LDAP登录过滤器
|
||||||
|
*/
|
||||||
|
private LdapFilter shiroLdapFilter(LdapAuthorizingRealm ldapAuthorizingRealm) {
|
||||||
|
LdapFilter bean = new LdapFilter();
|
||||||
|
bean.setAuthorizingRealm(ldapAuthorizingRealm);
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form登录过滤器
|
* Form登录过滤器
|
||||||
@@ -131,7 +143,7 @@ public class ShiroConfig {
|
|||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public ShiroFilterFactoryBean shiroFilter(WebSecurityManager webSecurityManager, AuthorizingRealm authorizingRealm,
|
public ShiroFilterFactoryBean shiroFilter(WebSecurityManager webSecurityManager, AuthorizingRealm authorizingRealm,
|
||||||
CasAuthorizingRealm casAuthorizingRealm) {
|
CasAuthorizingRealm casAuthorizingRealm, LdapAuthorizingRealm ldapAuthorizingRealm) {
|
||||||
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
|
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
|
||||||
bean.setSecurityManager(webSecurityManager);
|
bean.setSecurityManager(webSecurityManager);
|
||||||
bean.setLoginUrl(Global.getProperty("shiro.loginUrl"));
|
bean.setLoginUrl(Global.getProperty("shiro.loginUrl"));
|
||||||
@@ -139,6 +151,7 @@ public class ShiroConfig {
|
|||||||
Map<String, Filter> filters = bean.getFilters();
|
Map<String, Filter> filters = bean.getFilters();
|
||||||
filters.put("inner", shiroInnerFilter());
|
filters.put("inner", shiroInnerFilter());
|
||||||
filters.put("cas", shiroCasFilter(casAuthorizingRealm));
|
filters.put("cas", shiroCasFilter(casAuthorizingRealm));
|
||||||
|
filters.put("ldap", shiroLdapFilter(ldapAuthorizingRealm));
|
||||||
filters.put("authc", shiroAuthcFilter(authorizingRealm));
|
filters.put("authc", shiroAuthcFilter(authorizingRealm));
|
||||||
filters.put("logout", shiroLogoutFilter(authorizingRealm));
|
filters.put("logout", shiroLogoutFilter(authorizingRealm));
|
||||||
filters.put("perms", shiroPermsFilter());
|
filters.put("perms", shiroPermsFilter());
|
||||||
@@ -182,17 +195,31 @@ public class ShiroConfig {
|
|||||||
bean.setCasServerCallbackUrl(Global.getProperty("shiro.casClientUrl") + Global.getAdminPath() + "/login-cas");
|
bean.setCasServerCallbackUrl(Global.getProperty("shiro.casClientUrl") + Global.getAdminPath() + "/login-cas");
|
||||||
return bean;
|
return bean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LDAP安全认证实现类
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public LdapAuthorizingRealm ldapAuthorizingRealm(SessionDAO sessionDAO, CasOutHandler casOutHandler) {
|
||||||
|
LdapAuthorizingRealm bean = new LdapAuthorizingRealm();
|
||||||
|
JndiLdapContextFactory contextFactory = (JndiLdapContextFactory) bean.getContextFactory();
|
||||||
|
contextFactory.setUrl(Global.getProperty("shiro.ldapUrl"/*, "ldap://127.0.0.1:389"*/));
|
||||||
|
bean.setUserDnTemplate(Global.getProperty("shiro.ldapUserDn"/*, "uid={0},ou=users,dc=mycompany,dc=com"*/));
|
||||||
|
bean.setSessionDAO(sessionDAO);
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义Shiro安全管理配置
|
* 定义Shiro安全管理配置
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public WebSecurityManager webSecurityManager(AuthorizingRealm authorizingRealm, CasAuthorizingRealm casAuthorizingRealm,
|
public WebSecurityManager webSecurityManager(AuthorizingRealm authorizingRealm, CasAuthorizingRealm casAuthorizingRealm,
|
||||||
SessionManager sessionManager, CacheManager shiroCacheManager) {
|
LdapAuthorizingRealm ldapAuthorizingRealm, SessionManager sessionManager, CacheManager shiroCacheManager) {
|
||||||
WebSecurityManager bean = new WebSecurityManager();
|
WebSecurityManager bean = new WebSecurityManager();
|
||||||
Collection<Realm> realms = ListUtils.newArrayList();
|
Collection<Realm> realms = ListUtils.newArrayList();
|
||||||
realms.add(authorizingRealm); // 第一个为权限授权控制类
|
realms.add(authorizingRealm); // 第一个为权限授权控制类
|
||||||
realms.add(casAuthorizingRealm);
|
realms.add(casAuthorizingRealm);
|
||||||
|
realms.add(ldapAuthorizingRealm);
|
||||||
bean.setRealms(realms);
|
bean.setRealms(realms);
|
||||||
bean.setSessionManager(sessionManager);
|
bean.setSessionManager(sessionManager);
|
||||||
bean.setCacheManager(shiroCacheManager);
|
bean.setCacheManager(shiroCacheManager);
|
||||||
|
|||||||
@@ -332,6 +332,10 @@ shiro:
|
|||||||
# logoutUrl: ${shiro.casServerUrl}/logout?service=${shiro.loginUrl}
|
# logoutUrl: ${shiro.casServerUrl}/logout?service=${shiro.loginUrl}
|
||||||
# successUrl: ${shiro.casClientUrl}${adminPath}/index
|
# successUrl: ${shiro.casClientUrl}${adminPath}/index
|
||||||
|
|
||||||
|
# # LDAP 相关设置(标准版)
|
||||||
|
# ldapUrl: ldap://127.0.0.1:389
|
||||||
|
# ldapUserDn: uid={0},ou=users,dc=mycompany,dc=com
|
||||||
|
|
||||||
# 简单 SSO 登录相关配置
|
# 简单 SSO 登录相关配置
|
||||||
sso:
|
sso:
|
||||||
# 如果启用/sso/{username}/{token}单点登录,请修改此安全key并与单点登录系统key一致。
|
# 如果启用/sso/{username}/{token}单点登录,请修改此安全key并与单点登录系统key一致。
|
||||||
@@ -423,6 +427,7 @@ shiro:
|
|||||||
/druid/** = perms[sys:state:druid]
|
/druid/** = perms[sys:state:druid]
|
||||||
/bpm/modeler/** = perms[bpm:modeler]
|
/bpm/modeler/** = perms[bpm:modeler]
|
||||||
${adminPath}/login-cas = cas
|
${adminPath}/login-cas = cas
|
||||||
|
${adminPath}/login-ldap = ldap
|
||||||
${adminPath}/login = authc
|
${adminPath}/login = authc
|
||||||
${adminPath}/logout = logout
|
${adminPath}/logout = logout
|
||||||
${adminPath}/file/** = user
|
${adminPath}/file/** = user
|
||||||
|
|||||||
@@ -14,6 +14,9 @@
|
|||||||
<ul id="loginTab" class="nav nav-tabs">
|
<ul id="loginTab" class="nav nav-tabs">
|
||||||
<li class="active"><a href="#tab-1" data-toggle="tab" action="${ctx}/login">${text('账号登录')}</a></li>
|
<li class="active"><a href="#tab-1" data-toggle="tab" action="${ctx}/login">${text('账号登录')}</a></li>
|
||||||
<li><a href="#tab-2" data-toggle="tab" action="${ctxPath}/account/loginByValidCode">${text('手机登录')}</a></li>
|
<li><a href="#tab-2" data-toggle="tab" action="${ctxPath}/account/loginByValidCode">${text('手机登录')}</a></li>
|
||||||
|
<% if(isNotBlank(@Global.getConfig('shiro.ldapUrl'))){ %>
|
||||||
|
<li><a href="#tab-3" data-toggle="tab" action="${ctx}/login-ldap">${text('LDAP登录')}</a></li>
|
||||||
|
<% } %>
|
||||||
</ul>
|
</ul>
|
||||||
<% } %>
|
<% } %>
|
||||||
<#form:form id="loginForm" model="${user!}" action="${ctx}/login" method="post" class="tab-content">
|
<#form:form id="loginForm" model="${user!}" action="${ctx}/login" method="post" class="tab-content">
|
||||||
@@ -28,7 +31,7 @@
|
|||||||
data-msg-required="${text('请填写登录账号.')}" placeholder="${text('登录账号')}"
|
data-msg-required="${text('请填写登录账号.')}" placeholder="${text('登录账号')}"
|
||||||
value="${cookie('rememberUserCode')}"/>
|
value="${cookie('rememberUserCode')}"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group has-feedback tab-pane tab-1 active">
|
<div class="form-group has-feedback tab-pane tab-1 tab-3 active">
|
||||||
<span class="icon-lock form-control-feedback" title="${text('登录密码,鼠标按下显示密码')}"
|
<span class="icon-lock form-control-feedback" title="${text('登录密码,鼠标按下显示密码')}"
|
||||||
onmousedown="$('#password').attr('type','text')" onmouseup="$('#password').attr('type','password')"
|
onmousedown="$('#password').attr('type','text')" onmouseup="$('#password').attr('type','password')"
|
||||||
onmouseenter="$(this).removeClass('icon-lock').addClass('icon-eye')"
|
onmouseenter="$(this).removeClass('icon-lock').addClass('icon-eye')"
|
||||||
|
|||||||
Reference in New Issue
Block a user