spring security 一個驗證碼登錄例子

看完shiro,在看spring security感覺快了很多,最開始看spring security的時候,非常暈,看完我覺得spring security做了太多事,以至於程序員都不知道,是怎麼實現的,這樣的

後果就是 當出現錯誤,或者需要修改的時候感覺無從下手。


個人理解,若有錯誤,請指正。

spring security跟shiro類似,都是使用過濾器來認證和授權,不同的是spring seciruty是實現了一個過濾器鏈,每個請求都要經過,我們可以使用自動配置,這樣spring security自動幫我們配置了這一系列過濾器,也可以自定義過濾器放在它的過濾器鏈中。


驗證碼或密碼登錄,需要重新修改認證過濾器

package com.test.hello.security;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AbstractAuthenticationToken;
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.UsernamePasswordAuthenticationFilter;

public class KdUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter{
	 private boolean postOnly = true;
	 public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
	        if (this.postOnly && !request.getMethod().equals("POST")) {
	            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
	        }

	        String username = obtainUsername(request);
	        String password = obtainPassword(request);
	        String type = request.getParameter("j_type");

	        if (username == null) {
	            username = "";
	        }

	        if (password == null) {
	            password = "";
	        }
	        
	        if (type == null) {
	        	type = "1";
	        }

	        username = username.trim();

	        Authentication authRequest;
	        if(type.equals("1")){
	        	 authRequest = new UsernamePasswordAuthenticationToken(username, password);
	        }else{
	        	 authRequest = new KdUsernamePasswordAuthenticationToken(username, password,type);
	        }
	       
	        
	       

	        // Allow subclasses to set the "details" property
	        setDetails(request, (AbstractAuthenticationToken)authRequest);

	        return this.getAuthenticationManager().authenticate(authRequest);
	    }
	 
	    /**
	     * 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, AbstractAuthenticationToken authRequest) {
	        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
	    }



}

type爲2時候,使用驗證碼登錄,token- >provider ->

token

package com.test.hello.security;

import java.util.Collection;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

public class KdUsernamePasswordAuthenticationToken extends AbstractAuthenticationToken{

	 //~ Instance fields ================================================================================================

    /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private final Object principal;
    private Object credentials;
    private String type;

    //~ Constructors ===================================================================================================

    /**
     * This constructor can be safely used by any code that wishes to create a
     * <code>UsernamePasswordAuthenticationToken</code>, as the {@link
     * #isAuthenticated()} will return <code>false</code>.
     *
     */
    public KdUsernamePasswordAuthenticationToken(Object principal, Object credentials,String type) {
        super(null);
        this.principal = principal;
        this.credentials = credentials;
        this.type = type;
        setAuthenticated(false);
    }

    /**
     * This constructor should only be used by <code>AuthenticationManager</code> or <code>AuthenticationProvider</code>
     * implementations that are satisfied with producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>)
     * authentication token.
     *
     * @param principal
     * @param credentials
     * @param authorities
     */
    public KdUsernamePasswordAuthenticationToken(Object principal, Object credentials,String type, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        this.type = type;
        super.setAuthenticated(true); // must use super, as we override
    }


    //~ Methods ========================================================================================================

    public Object getCredentials() {
        return this.credentials;
    }

    public Object getPrincipal() {
        return this.principal;
    }

    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        if (isAuthenticated) {
            throw new IllegalArgumentException(
                "Once created you cannot set this token to authenticated. Create a new instance using the constructor which takes a GrantedAuthority list will mark this as authenticated.");
        }

        super.setAuthenticated(false);
    }

    @Override
    public void eraseCredentials() {
        super.eraseCredentials();
        credentials = null;
    }

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	
	

}

provider 重寫了  密碼校驗方法,並且默認使用了KdJdbcDaoImpl去查詢用戶信息

KdAbstractUserDetailsAuthenticationProvider跟AbstractUserDetailsAuthenticationProvider一樣僅僅改了authenticate方法裏面的

 Assert.isInstanceOf(KdUsernamePasswordAuthenticationToken.class,

package com.test.hello.security;

import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

public class KdDaoAuthenticationProvider extends KdAbstractUserDetailsAuthenticationProvider{
	
	private UserDetailsService userDetailsService = new KdJdbcDaoImpl();

	public UserDetailsService getUserDetailsService() {
		return userDetailsService;
	}

	public void setUserDetailsService(UserDetailsService userDetailsService) {
		this.userDetailsService = userDetailsService;
	}

	@Override
	public boolean supports(Class<?> authentication) {
		return (KdUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
	}

	@SuppressWarnings("deprecation")
	@Override
	protected void additionalAuthenticationChecks(UserDetails userDetails,
			KdUsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		 if (authentication.getCredentials() == null) {
	            logger.debug("Authentication failed: no credentials provided");

	            throw new BadCredentialsException(messages.getMessage(
	                    "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);
	        }

	        String presentedPassword = authentication.getCredentials().toString();

	        if (!userDetails.getPassword().equals(presentedPassword)) {
	            logger.debug("Authentication failed: password does not match stored value");

	            throw new BadCredentialsException(messages.getMessage(
	                    "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);
	        }else{
	        	KdJdbcDaoImpl.userpass.remove(userDetails.getUsername());
	        }
		
	}
	
	

	

	@Override
	protected UserDetails retrieveUser(String username,
			KdUsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		 UserDetails loadedUser;

	        try {
	            loadedUser = this.getUserDetailsService().loadUserByUsername(username);
	        } catch (UsernameNotFoundException notFound) {
	            throw notFound;
	        } catch (Exception repositoryProblem) {
	            throw new InternalAuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
	        }

	        if (loadedUser == null) {
	            throw new InternalAuthenticationServiceException(
	                    "UserDetailsService returned null, which is an interface contract violation");
	        }
	        return loadedUser;
	}
	
	
}

KdJdbcDaoImpl

package com.test.hello.security;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;

public class KdJdbcDaoImpl extends JdbcDaoImpl{
	
	public static ConcurrentHashMap<String,String> userpass = new ConcurrentHashMap<String, String>();

	@Override
	protected List<UserDetails> loadUsersByUsername(String username) {
		List<UserDetails> list = new ArrayList<UserDetails>();
		 String password = userpass.get(username);
		 if(password != null){
			 list.add(new User(username, password, true, true, true, true, AuthorityUtils.NO_AUTHORITIES));
		 }
		
		 return list;
	}
	
	
	 


}

最後的配置

<security:http pattern="/resource/**" security="none"></security:http>
     <security:http pattern="/index.jsp" security="none"></security:http>
      <security:http pattern="/" security="none"></security:http>
       <security:http pattern="/checkcode" security="none"></security:http>
     <security:http  entry-point-ref="loginUrlAuthenticationEntryPoint" >
        <security:intercept-url pattern="/role/user**" access="ROLE_USER"/>
        <security:intercept-url pattern="/role/admin**" access="ROLE_ADMIN"/>
        <security:intercept-url pattern="/role/manager**" access="ROLE_MANAGER"/>
        <security:custom-filter ref="kdUsernamePasswordAuthenticationFilter" position="FORM_LOGIN_FILTER"/>
		<security:custom-filter ref="logoutFilter" position="LOGOUT_FILTER"/>
		<security:access-denied-handler ref="accessDeniedHandlerImpl"/>
     </security:http> 
   
  
     <bean id="accessDeniedHandlerImpl" class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
      <property name="errorPage" value="/errorPage.jsp"></property>
     </bean>
     <bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
	 <property name="loginFormUrl" value="/index.jsp"></property>
	</bean>
	
    <bean id="kdUsernamePasswordAuthenticationFilter" class="com.test.hello.security.KdUsernamePasswordAuthenticationFilter">
    <property name="authenticationManager" ref="org.springframework.security.authenticationManager"></property>
    <property name="authenticationSuccessHandler" ref="savedRequestAwareAuthenticationSuccessHandler"></property>
    <property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler"></property>
    </bean>
    
    <bean id="savedRequestAwareAuthenticationSuccessHandler" class="com.test.hello.security.KdSavedRequestAwareAuthenticationSuccessHandler">
    <property name="defaultTargetUrl" value="/loginSuccess"></property>
    </bean>
    <bean id="simpleUrlAuthenticationFailureHandler" class="com.test.hello.security.KdSimpleUrlAuthenticationFailureHandler">
    <property name="defaultFailureUrl" value="/index.jsp"></property>
    </bean>
    
    <bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
    <property name="filterProcessesUrl" value="/logout"></property>
    <constructor-arg index="0" >
     <bean class="org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler">
      <property name="defaultTargetUrl" value="/index.jsp"></property>
     </bean>
    </constructor-arg>
    <constructor-arg index="1">
      <array>
        <bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"></bean>
      </array>
    </constructor-arg>
    </bean>
     
     
      <bean id="org.springframework.security.authenticationManager" name="authenticationManager" class="org.springframework.security.authentication.ProviderManager" >  
            <property name="authenticationEventPublisher" ref="defaultAuthenticationEventPublisher"></property>  
            <property name="providers">  
                <list>  
                    <ref bean="daoAuthenticationProvider"/>  
                    <ref bean="anonymousAuthenticationProvider"/>  
                    <ref bean="kdDaoAuthenticationProvider"/>
                </list>  
            </property>  
        </bean>  
          
        <bean id="defaultAuthenticationEventPublisher" class="org.springframework.security.authentication.DefaultAuthenticationEventPublisher"></bean>  
          
        <bean id="anonymousAuthenticationProvider" class="org.springframework.security.authentication.AnonymousAuthenticationProvider">  
            <property name="key" value="work"></property>  
        </bean>  
          
        <bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">  
            <property name="userDetailsService" ref="userService"></property>  
           <!--  <property name="passwordEncoder" ref="bcrypt"></property> -->
        </bean>  
        <bean id="userService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
        </bean>
        
        <bean id="kdDaoAuthenticationProvider" class="com.test.hello.security.KdDaoAuthenticationProvider">
        <property name="userDetailsService" ref="kdJdbcDaoImpl"></property>
        </bean>
        
        <bean id="kdJdbcDaoImpl" class="com.test.hello.security.KdJdbcDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
        </bean>
	

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>My JSP 'index.jsp' starting page</title>
    
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<script type="text/javascript" src="resource/js/jquery.min.js"></script>
	<script type="text/javascript">
	$(function(){
		$("#jtypese").change(function(){
			 var v = $("#jtypese").val();
			 if(v=='2'){
				 var uname = $("#usernameinput").val();
				 var url = "<%=basePath%>checkcode?username="+uname;
				 $.get(url,function(data,status){
					    alert("驗證碼: " + data + "\n發送狀態: " + status);
					  });
			 }
		});
	});
	</script>

  </head>
  
  <body>
    This is my login page. <br>
    <form action="j_spring_security_check" method="post">
    <table>
    <tr><td>用戶名:</td><td><input name="j_username" value="u1" id="usernameinput"></td></tr>
    <tr><td>密碼/驗證碼:</td><td><input name="j_password" value="p1"></td></tr>
    <tr><td>登錄方式: </td><td><select name="j_type" id="jtypese"><option value="1">密碼</option><option value="2">驗證碼</option></select></td></tr>
    <tr><td colspan="2"> <input type="submit" value="submit"></td></tr>
    </table>
    </form>
    
   異常: ${SPRING_SECURITY_LAST_EXCEPTION }<br>
  失敗次數:  ${SPRING_SESSION_FAIL_TIMES }
  </body>
</html>


發佈了68 篇原創文章 · 獲贊 9 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章