序:这里主要对springmvc 项目中用spring-security和shiro两种连接LDAP的配置做说明
用来实现ldap对用户登录及权限的管理
一:spring security方式
(1)配置web.xml和导jar包:这里用maven 导入(spring-ldap-core spring-ldap-core-tiger spring-security-ldap spring-security-core spring-security-web spring-security-config spring-security-taglibs,这些都是1.31版本的jar)
可能有些包不用,但是我都加了 (礼多人不怪)
在web.xml里加入如下
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(2).配置spring-security文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:s="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<s:http>
<!-- <s:intercept-url pattern="/testttt" access="ROLE_ADMIN" /> -->
<s:intercept-url pattern="/admin/**" access="ROLE_USER" /> <!—所有请求admin开头的都必须是ROLE_USER角色的用户
<s:intercept-url pattern="/captcha-p_w_picpath"
access="IS_AUTHENTICATED_ANONYMOUSLY" /> <!—若请求captcha-p_w_picpath,则不做验证
<s:anonymous />
<!- 配置一个自定义的登录拦截器,因为默认的登录拦截器不能为我们处理验证码等其他登录问题-
<s:custom-filter position="FORM_LOGIN_FILTER" ref="authenticationFilter" />
<!--访问登录拦截器 -->
<s:http-basic entry-point-ref="authenticationEntryPoint" />
<!-- 单点登录配置 -->
<s:custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
<s:session-management session-authentication-strategy-ref="sas" />
<!-- 没有权限不够角色时访问某个资源 处理的配置 -->
<s:access-denied-handler ref="accessDeniedHandler"/>
</s:http>
<!-进行登录认证配置 authenticationManager 比较重要-
<s:authentication-manager alias="authenticationManager">
<s:ldap-authentication-provider
group-search-filter="member={0}" group-search-base="ou=groups"
user-search-base="ou=people" user-search-filter="uid={0}" />
<s:authentication-provider ref='secondLdapProvider' />
</s:authentication-manager>
<!-- 401 密码错误 403 没有权限 404 页面没找到 413 上传的文件或数据过大 -->
<!-- 此为权限不够角色,登录失败时异常拦截器 -->
<bean id="exceptionTranslationFilter"
class="org.springframework.security.web.access.ExceptionTranslationFilter">
<property name="authenticationEntryPoint" ref="authenticationEntryPoint" />
<property name="accessDeniedHandler" ref="accessDeniedHandler" />
</bean>
<!-- 登录页面入口 -->
<bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/sign_in" />
</bean>
<!-- 在没有权限不够角色访问此资源时跳转入口 -->
<bean id="accessDeniedHandler"
class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<property name="errorPage" value="/authority.html" />
</bean>
<!-调用自定义登录拦截器MySpecialAuthenticationFilter,并配置 authenticationManager登录认证及其他认证-
<bean id="authenticationFilter" class="com.zqgame.util.MySpecialAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
<!-- 调用单点登录认证配置 -->
<property name="sessionAuthenticationStrategy" ref="sas" />
<property name="filterProcessesUrl" value="/auth" /><!-- 调用处理登录的action -->
<property name="authenticationSuccessHandler" ref="successHandler" /><!--
调用登录认证成功后的处理 -->
<property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler" /><!--
调用登录失败后的处理配置 -->
</bean>
<!--登录认证成功后的处理
<bean id="successHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/body/adminmain" />
</bean>
<!--登录失败后的处理配置
<bean id="simpleUrlAuthenticationFailureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/login_error" />
</bean>
<!-- 单点登录配置 begin -->
<bean id="concurrencyFilter"
class="org.springframework.security.web.session.ConcurrentSessionFilter">
<property name="sessionRegistry" ref="sessionRegistry" />
<property name="expiredUrl" value="/session-expired.html" />
</bean>
<bean id="sas"
class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
<constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<property name="maximumSessions" value="1" />
</bean>
<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
<!-- 单点登录配置 end -->
<!-下面就是根据自己所要连接的ldap具体情况来做ldap的本身配置了-
<bean id="contextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://180.96.13.102:389/dc=zqgame,dc=com" />
</bean>
<bean id="secondLdapProvider"
class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<constructor-arg>
<bean
class="org.springframework.security.ldap.authentication.BindAuthenticator">
<constructor-arg ref="contextSource" />
<property name="userSearch">
<bean id="userSearch"
class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0" value="ou=people" />
<constructor-arg index="1" value="(uid={0})" />
<constructor-arg index="2" ref="contextSource" />
</bean>
</property>
</bean>
</constructor-arg>
<constructor-arg>
<bean
class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
<constructor-arg ref="contextSource" />
<constructor-arg value="ou=groups" />
<property name="groupSearchFilter" value="(member={0})" />
<property name="rolePrefix" value="ROLE_" />
<property name="searchSubtree" value="true" />
<property name="convertToUpperCase" value="true" />
</bean>
</constructor-arg>
</bean>
</beans>
(3) 自定义拦截器com.zqgame.util.MySpecialAuthenticationFilter
这个拦截器就是做ldap登录认证的,里面包括了一些用户输入信息正确性判断和验证码判断(所有不能登录情况都是异常捕获)AuthenticationServiceException会根据上面的配置文件中登录错误的配置而跳转
package com.zqgame.util;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
public class MySpecialAuthenticationFilter extends AbstractAuthenticationProcessingFilter{
public MySpecialAuthenticationFilter() {
super("/auth");//这个auth就是外面要访问这个拦截器的入口
}
public MySpecialAuthenticationFilter(String defaultFilterProcessesUrl) {
super("/auth");
}
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
HttpSession session = request.getSession();
String username = obtainUsername(request);
String password = obtainPassword(request);
String catchImage = request.getParameter("captcha");//验证码
//判断用户名登录密码是否为空
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
session.setAttribute("loginError", "请输入用户名或者密码!");
throw new AuthenticationServiceException("j_username or j_password is not null!"); //跳转到login_error下
}
// 从session中获得验证码,与用户输入的验证码做比较
String sessionCaptcha = (String) session.getAttribute(Constant.CAPTCHA);
if (StringUtils.isEmpty(catchImage) || StringUtils.isEmpty(sessionCaptcha)) {
session.setAttribute("loginError", "验证码为空!");
throw new AuthenticationServiceException("the session captcha is not null!");//跳转到login_error下
}
session.removeAttribute(Constant.CAPTCHA);// 清空Sesion里的验证码
//验证码输入校验
if (!sessionCaptcha.equalsIgnoreCase(catchImage)) {
session.setAttribute("loginError", "验证码输入错误!");
throw new AuthenticationServiceException("the captcha is error!");//跳转到login_error下
}
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
setDetails(request, authRequest);
Authentication authen = null;
//执行ldap的登录验证
try {
authen = this.getAuthenticationManager().authenticate(authRequest);
} catch (AuthenticationException failed) {
session.setAttribute("loginError", "用户名或密码错误!");
throw new AuthenticationServiceException("the captcha is error!");//跳转到login_error
}
session.setAttribute("managerSession", username);
return authen;
}
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(passwordParameter);
}
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(usernameParameter);
}
protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
}
(4)登录Action类
package com.zqgame.controllers.admin;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.zqgame.common.Constant;
import com.zqgame.models.SignIn;
import com.zqgame.services.UserService;
import com.zqgame.util.Digest;
/**
* 后台登录控制器
* @author User
*/
@Controller
public class SessionsController{
/**
* 进入登录页面
* @return
*/
@RequestMapping(value = "/sign_in", method = RequestMethod.GET)
public String newForm(Model model) {
return "admin/sessions/new";
}
/**
* 登录错误
* @return
*/
@RequestMapping(value = "/login_error", method = RequestMethod.GET)
public String loginError(Model model) {
return "admin/sessions/new";
}
/**
* 退出登录 logout() 销毁会话
* @param request
* @return
*/
@RequestMapping(value = "signout")
public void signOut(HttpServletRequest request, HttpServletResponse response) {
try {
SecurityContextLogoutHandler securityLogout = new SecurityContextLogoutHandler();
securityLogout.logout(request, response, null);
request.getRequestDispatcher(
"WEB-INF/views/admin/sessions/loginout.jsp").forward(
request, response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
(5)登录jsp页面form部分
<form action="/auth" id="loginForm" method="post"> //auth就是那个自定义拦截器的访问路径,而不是去action
<div class="box">
<c:if test="${!empty sessionScope.loginError}">
<fmt:message key='${sessionScope.loginError}' />
<c:remove var="loginError" scope="session" />//删除session中存储的错误提示
用户:<input type="text" class="inp1" name="j_username" id="username" size="12" maxlength="50" value="${signIn.j_username}"/>
<br>
密码:<input type="password" class="inp2" name="j_password" id="password" size="12" maxlength="50"/>
<br>
验证码:<input type="text" name="captcha" id="captcha" class="login_input required" MaxLength="6" style="width:55px;text-transform:uppercase;"/>
<br>这个验证码具体怎么生成的这里不做说明
<input type="submit">登 录</input>
</form>
上面就是整个LDAP与spring-security的配置了,当然这里只是我自己的处理方式,可能还有很多不好的地方,希望拍砖
二.Shiro 方式
(1)配置web.xml和导包(shiro-core shiro-web shiro-spring cglib-nodep 版本1.2.0)
Web.xml里加入
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(2)Shiro配置文件(文档里有注释,这个配置文件基本上不用改啥)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/sign_in"/>
<property name="successUrl" value="/body/adminmain"/>
<property name="filterChainDefinitions"><!—配置请求需要处理方式
<value>
/sign_in = anon
/admin/** = authc <!--admin的url全部要能够被验证通过-->
/sign_up = anon
/captcha-p_w_picpath = anon
/sessions/** = anon
</value>
</property>
</bean>
<bean id="sha512Matcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="${shiro.hashAlgorithmName}" />
<property name="storedCredentialsHexEncoded" value="${shiro.storedCredentialsHexEncoded}" />
<property name="hashIterations" value="${shiro.hashIterations}" />
</bean>
<bean id="openldapRealm" class="org.apache.shiro.realm.ldap.JndiLdapRealm">
<!-配置登录认证访问ldap的树域-
<property name="userDnTemplate" value="cn={0},dc=zqgame,dc=com"/>
<property name="contextFactory" ref="contextFactory" />
</bean>
<!-配置ldap路径及配置一个默认的用户和密码-
<bean id="contextFactory" class="org.apache.shiro.realm.ldap.JndiLdapContextFactory">
<property name="url" value="ldap://180.96.13.102:389/??sub"/>
<property name="systemUsername" value="ldapuser"/>
<property name="systemPassword" value="zqgame.com"/>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="openldapRealm" />
<property name="sessionMode" value="native"/>
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
</beans>
(3)登录action(这里action不用那么麻烦,所以这里就把登录的action和退出的action写上吧)
/*执行登录*/
public String create(@Valid SignIn signIn, BindingResult result, HttpSession session, Model model) {
if (result.hasErrors()) {
model.addAttribute("loginError", "请输入内容!");
model.addAttribute("signIn", signIn);
return "admin/sessions/new";
}
//从session中获得验证码
String sessionCaptcha = (String) session.getAttribute(Constant.CAPTCHA);
if(StringUtils.isEmpty(sessionCaptcha)) {
model.addAttribute("loginError", "session里的验证码为空!");
return "admin/sessions/new";
}
session.removeAttribute(Constant.CAPTCHA);//清空验证码
if(!sessionCaptcha.equalsIgnoreCase(signIn.getCaptcha())) {
model.addAttribute("loginError", "验证码输入错误!");
return "admin/sessions/new";
}
/*shiro执行ldap登录认证*/
UsernamePasswordToken token = new UsernamePasswordToken(signIn.getUsername(), signIn.getPassword());
token.setRememberMe(true);
Subject currentUser = SecurityUtils.getSubject();
try {
currentUser.login(token);
session.setAttribute(Constant.MANAGER_SESSION, signIn.getUsername());
}catch (Exception ice) {
result.reject("mismatch.signIn.password");
//password didn't match, try again?
}
if (currentUser.isAuthenticated()) {//登录成功进入主页
return "redirect:/body/adminmain";
}
model.addAttribute("loginError", "您输入的用户名或密码错误!");
return "admin/sessions/new";
}
/**
* 退出登录
* logout() 销毁回话
* @return
*/
@RequestMapping(value = "signout", method = RequestMethod.GET)
public void signOut(HttpServletRequest request, HttpServletResponse response) {
SecurityUtils.getSubject().logout();
try {
request.getRequestDispatcher("WEB-INF/views/admin/sessions/loginout.jsp").forward(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
到此两种方式都可以使用了,至于那个配置文件,可以把他放到web.xml里加载就行,就跟加载spring的文件一样就行
另:ldap本身的性质还在研究,不会哦