spring-security 多型別使用者登入+登入多引數驗證
如果一個系統分為前臺使用者和後臺使用者那麼就不能使用spring-security的預設配置了。 需要自己來分開配置兩種使用者的登入方式。
首先建立spring-disuser-security.xml 與 spring-etuser-security.xml 兩個配置檔案,分別來配置兩種使用者登入的許可權與驗證方式
spring-disuser-security.xml的內容如下
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- 使用自定義Filter時需要將http節點的auto-config="true"屬性刪除,並且要通過entry-point-ref指定一個入口 -->
<security:http use-expressions="true" access-denied-page="/powermiss.jsp"
authentication-manager-ref="disuserAuthManager" name="disuserSecurity"
entry-point-ref="authenticationEntryPoint">
<security:intercept-url pattern="/disuserlogin.jsp" access="permitAll"/>
<!-- 配置自定義的Filter,並且將其放在FORM_LOGIN_FILTER節點,就會替換掉原來的FORM_LOGIN_FILTER節點 -->
<security:custom-filter ref="loginProcessFilter" position="FORM_LOGIN_FILTER"/>
</security:http>
<security:authentication-manager id="disuserAuthManager" >
<security:authentication-provider user-service-ref="DisuserserDetailService" />
</security:authentication-manager>
<!-- 登入處理Filter -->
<bean id="loginProcessFilter" class="com.tuanfang.service.DisUsernamePasswordAuthenticationFilter">
<property name="loginid" value="loginid" />
<property name="yzm" value="yzm" />
<property name="usernameParameter" value="username" />
<property name="passwordParameter" value="password" />
<property name="filterProcessesUrl" value="/disuserlogin.htm" />
<property name="authenticationManager" ref="disuserAuthManager" />
<property name="authenticationSuccessHandler" ref="successHandler" />
<property name="authenticationFailureHandler" ref="failureHandler" />
</bean>
<!-- 登入成功處理 -->
<bean id="successHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/index.jsp" />
<property name="alwaysUseDefaultTargetUrl" value="true" />
</bean>
<!-- 登入失敗處理 -->
<bean id="failureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/disuserlogin.jsp?error=true" />
</bean>
<!-- 登入入口 -->
<bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/disuserlogin.jsp" />
</bean>
</beans>
接下來編寫DisUsernamePasswordAuthenticationFilter.java檔案,處理使用者登入。
spring-security預設的處理登入的類是org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter,可以將其中的程式碼複製到自己寫的DisUsernamePasswordAuthenticationFilter.java檔案中。
然後在進行自己的修改,達到驗證驗證碼,與公司登入id的驗證。
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.tuanfang.service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationServiceException;
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;
import org.springframework.util.Assert;
/**
* Processes an authentication form submission. Called {@code AuthenticationProcessingFilter} prior to Spring Security
* 3.0.
* <p>
* Login forms must present two parameters to this filter: a username and
* password. The default parameter names to use are contained in the
* static fields {@link #SPRING_SECURITY_FORM_USERNAME_KEY} and {@link #SPRING_SECURITY_FORM_PASSWORD_KEY}.
* The parameter names can also be changed by setting the {@code usernameParameter} and {@code passwordParameter}
* properties.
* <p>
* This filter by default responds to the URL {@code /j_spring_security_check}.
*
* @author Ben Alex
* @author Colin Sampaleanu
* @author Luke Taylor
* @since 3.0
*/
public class DisUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
//~ Static fields/initializers =====================================================================================
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password";
public static final String SPRING_SECURITY_FORM_YZM_KEY = "yzm";
public static final String SPRING_SECURITY_FORM_LOGINID_KEY = "loginid";
public static final String USERNAME_LOGINID_SPLIT = "/";
/**
* @deprecated If you want to retain the username, cache it in a customized {@code AuthenticationFailureHandler}
*/
@Deprecated
public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
private String yzm = SPRING_SECURITY_FORM_YZM_KEY;
private String loginid = SPRING_SECURITY_FORM_LOGINID_KEY;
private boolean postOnly = true;
//~ Constructors ===================================================================================================
public DisUsernamePasswordAuthenticationFilter() {
super("/j_spring_security_check");
}
//~ Methods ========================================================================================================
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
String loginid = obtainLoginid(request);
String yzm = obtainYzm(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
if (loginid == null) {
loginid = "";
}
if (yzm == null) {
yzm = "";
}
username = username.trim();
password = password.trim();
loginid = loginid.trim();
yzm = yzm.trim();
username = loginid + USERNAME_LOGINID_SPLIT + username ; //將公司登入id與登入使用者名稱使用 / 連線起來
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
/**
* Enables subclasses to override the composition of the password, such as by including additional values
* and a separator.<p>This might be used for example if a postcode/zipcode was required in addition to the
* password. A delimiter such as a pipe (|) should be used to separate the password and extended value(s). The
* <code>AuthenticationDao</code> will need to generate the expected password in a corresponding manner.</p>
*
* @param request so that request attributes can be retrieved
*
* @return the password that will be presented in the <code>Authentication</code> request token to the
* <code>AuthenticationManager</code>
*/
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(passwordParameter);
}
//獲取驗證碼
protected String obtainYzm(HttpServletRequest request){
return request.getParameter(yzm);
}
//獲取公司id
protected String obtainLoginid(HttpServletRequest request){
return request.getParameter(loginid);
}
/**
* Enables subclasses to override the composition of the username, such as by including additional values
* and a separator.
*
* @param request so that request attributes can be retrieved
*
* @return the username that will be presented in the <code>Authentication</code> request token to the
* <code>AuthenticationManager</code>
*/
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(usernameParameter);
}
/**
* Provided so that subclasses may configure what is put into the authentication request's details
* property.
*
* @param request that an authentication request is being created for
* @param authRequest the authentication request object that should have its details set
*/
protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
/**
* Sets the parameter name which will be used to obtain the username from the login request.
*
* @param usernameParameter the parameter name. Defaults to "j_username".
*/
public void setUsernameParameter(String usernameParameter) {
Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
this.usernameParameter = usernameParameter;
}
/**
* Sets the parameter name which will be used to obtain the password from the login request..
*
* @param passwordParameter the parameter name. Defaults to "j_password".
*/
public void setPasswordParameter(String passwordParameter) {
Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
this.passwordParameter = passwordParameter;
}
/**
* Defines whether only HTTP POST requests will be allowed by this filter.
* If set to true, and an authentication request is received which is not a POST request, an exception will
* be raised immediately and authentication will not be attempted. The <tt>unsuccessfulAuthentication()</tt> method
* will be called as if handling a failed authentication.
* <p>
* Defaults to <tt>true</tt> but may be overridden by subclasses.
*/
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
public final String getUsernameParameter() {
return usernameParameter;
}
public final String getPasswordParameter() {
return passwordParameter;
}
public String getYzm() {
return yzm;
}
public void setYzm(String yzm) {
this.yzm = yzm;
}
public String getLoginid() {
return loginid;
}
public void setLoginid(String loginid) {
this.loginid = loginid;
}
}
接下來編寫提供Disuser的UserDetailsService類
package com.tuanfang.service;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Repository;
import com.tuanfang.dao.impl.DisuserDaoImpl;
import com.tuanfang.dao.impl.SysPowrDaoImpl;
import com.tuanfang.pojo.Disuser;
import com.tuanfang.pojo.SysPower;
@Repository("DisuserserDetailService")
public class DisuserserDetailService implements UserDetailsService{
@Resource(name="DisuserDaoImpl")
private DisuserDaoImpl disuserDao ;
@Resource(name="SysPowrDaoImpl")
private SysPowrDaoImpl sysPowrDao ;
@Override
public UserDetails loadUserByUsername(String usernameAndloginId)
throws UsernameNotFoundException {
//將公司登入id與登入使用者名稱使用/分開
String args[] = usernameAndloginId.split(DisUsernamePasswordAuthenticationFilter.USERNAME_LOGINID_SPLIT);
String loginid = args[0];
String username = args[1];
Disuser disuser = disuserDao.findByLoginIdAndUserName(loginid , username);
UserDetails userDetail = null ;
if(disuser != null){
userDetail = new User(username, disuser.getPassword(),disuser.getStatus() ==Disuser.ENABLE ,
true, true, true, obtainUserPowers(disuser.getId()));
}
return userDetail;
}
public Collection<GrantedAuthority> obtainUserPowers(BigInteger userId){
Collection<GrantedAuthority> gas = new ArrayList<GrantedAuthority>();
List<SysPower> powers = sysPowrDao.findDisuserPowers(userId);
if(powers != null && powers.size() > 0){
for (SysPower sysPower : powers) {
gas.add(new SimpleGrantedAuthority(sysPower.getCode()));
}
}
gas.add(new SimpleGrantedAuthority("HAVE_LOGIN")); //登入
return gas ;
}
}
登入頁面disuserlogin.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>DisuserLogin</title>
</head>
<body>
<form action="<%=basePath %>disuserlogin.htm" method="post">
<p>
<label for="username">Username</label> <input id="username"
name="username" type="text" />
</p>
<p>
<label for="password">password</label> <input id="password"
name="password" type="password" />
</p>
<p>
<label for="loginid">LoginId</label> <input id="loginid"
name="loginid" type="text" />
</p>
<p>
<label for="yzm">驗證碼</label> <input id="yzm"
name="yzm" type="text" />
</p>
<input type="submit" value="Login" />
</form>
</body>
</html>
這樣就配置好了Disuser的login
接下來配置EtdsUser的login。只需要username與password
spring-etuser-security.xml檔案內容如下(使用spring-security預設的方式配置):
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<security:http auto-config="true" pattern="/admin/**" use-expressions="true" access-denied-page="/powermiss.jsp"
authentication-manager-ref="etuserAuthManager" name="etuserSecurity">
<security:intercept-url pattern="/admin/etuserlogin.jsp" access="permitAll"/>
<security:form-login login-processing-url="/admin/j_spring_security_check" authentication-failure-url="/admin/etuserlogin.jsp?error=true" login-page="/admin/etuserlogin.jsp"
default-target-url="/index.jsp" />
</security:http>
<security:authentication-manager id="etuserAuthManager" >
<security:authentication-provider user-service-ref="EtuserDetailService"/>
</security:authentication-manager>
</beans>
Etdsuser的UserDetailsService類如下:
package com.tuanfang.service;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Repository;
import com.tuanfang.dao.impl.EtdsuserDaoImpl;
import com.tuanfang.dao.impl.SysPowrDaoImpl;
import com.tuanfang.pojo.EtdsUser;
import com.tuanfang.pojo.SysPower;
@Repository("EtuserDetailService")
public class EtuserDetailService implements UserDetailsService{
@Resource(name="EtdsuserDaoImpl")
private EtdsuserDaoImpl etdsuserDao ;
@Resource(name="SysPowrDaoImpl")
private SysPowrDaoImpl sysPowrDao ;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
EtdsUser etuser = etdsuserDao.findUserByLoginName(username);
UserDetails userDetail = null ;
if(etuser != null){
userDetail = new User(username, etuser.getPassword(),etuser.getStatus() ==EtdsUser.ENABLE ,
true, true, true, obtainUserPowers(etuser.getId()));
}
return userDetail;
}
public Collection<GrantedAuthority> obtainUserPowers(BigInteger userId){
Collection<GrantedAuthority> gas = new ArrayList<GrantedAuthority>();
List<SysPower> powers = sysPowrDao.findEtuserPowers(userId);
if(powers != null && powers.size() > 0){
for (SysPower sysPower : powers) {
gas.add(new SimpleGrantedAuthority(sysPower.getCode()));
}
}
gas.add(new SimpleGrantedAuthority("HAVE_LOGIN")); //登入
return gas ;
}
}
然後在applicationContext.xml中匯入spring-disuser-security.xml與spring-etuser-security.xml