`
guoyiqi
  • 浏览: 966411 次
社区版块
存档分类
最新评论

Spring Security 设置session

阅读更多

使用SecurityContextHolder来偷窥登入帐号密码,手段还真是不够文雅。 Spring-Security3是有提供取得登入资讯塞到Session的实践,不过写起来很烦,很烦也大概不易被破解^^。 Google这方面的资讯,不是缺漏,就是讲述古早的版本,还有中文网站,资讯虽新,却常出现文章一大抄的谬误,我目前是用3.0.2版,和3.0.1、3.0 .0差异何在也不知,不过至少我这方面有踹通,实践方式和Google内容还是有些关键性的差异:

第一个应该是security-context.xml的设定了:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
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-3.0.xsd">
    
<http use-expressions="true" auto-config="true">
        
<intercept-url pattern="/*.do" access="hasRole('ROLE_USER')"/>
        
<intercept-url pattern="/login.jsp" access="isAnonymous()"/>
        
<intercept-url pattern="/**" access="permitAll"/>
        
<form-login login-processing-url="/j_spring_security_check"
         
login-page="/login.jsp" default-target-url="/echo.do"
         
authentication-failure-url="/login.jsp?error=1"/>
        
<logout logout-url="/j_spring_security_logout" logout-success-url="/login.jsp"/>
        
<custom-filter before="FORM_LOGIN_FILTER" ref="authenticationProcessingFilter"/>
        
<session-management invalid-session-url="/timeout.html">
            
<concurrency-control max-sessions="1" />
        
</session-management>
    
</http>
    
<authentication-manager alias="authenticationManager">
        
<authentication-provider user-service-ref="securityManager"/>
    
</authentication-manager>
    
<beans:bean id="securityManager"
        
class="com.foo.dao.impl.UserDetailsS​​erviceImpl"/>
    
<beans:bean id="authenticationProcessingFilter"
        
class="org.springframework.security.web.authentication.UsernamePasswordAut​​henticationFilter">
        
<beans:property name="authenticationManager" ref="authenticationManager"/>
        
<beans:property name="authenticationSuccessHandler">
            
<beans:bean class="com.foo.security.MyAuthenticationSuccessHandler">
                
<beans:property name="d​​efaultTargetUrl" value="/echo.do"/>
            
</beans:bean>
        
</beans:property>
        
<beans:property name="filterProcessesUrl" value="/j_spring_security_check"/>
    
</beans:bean>
</beans:beans>
 

红字是之前SecurityContextHolder饭粒多出来的设定,真正是靠腰的多。 <http>标签多出<custom-filter>指向authenticationProcessingFilter这个bean,而Google的before后所接的定字几乎都错,都是这么写:

<custom-filter before="AUTHENTICATION_PROCESSING_FILTER" ref="authenticationProcessingFilter"/>

可是Filter的列举(枚举)型态里,根本没这有AUTHENTICATION_PROCESSING_FILTER这号人物,其它的参考下列表,我原本用CONCURRENT_SESSION_FILTER过不了关,后来改成FORM_LOGIN_FILTER才Pass,Spring-Seucrity 3这部份很像Struts2的Interceptor Stack,从下表来看,应该​​是有顺序,LOGOUT_FILTER排在FORM_LOGIN_FILTER之前…嗯~~也蛮合理的。

Enumerated Values​​ :
    
- FIRST
    
- CHANNEL_FILTER
    
- CONCURRENT_SESSION_FILTER
    
- SECURITY_CONTEXT_FILTER
    
- LOGOUT_FILTER
    
- X509_FILTER
    
- PRE_AUTH_FILTER
    
- CAS_FILTER
    
- FORM_LOGIN_FILTER
    
- OPENID_FILTER
    
- BASIC_AUTH_FILTER
    
- SERVLET_API_SUPPORT_FILTER
    
- REMEMBER_ME_FILTER
    
- ANONYMOUS_FILTER
    
- EXCEPTION_TRANSLATION_FILTER
    
- SESSION_MANAGEMENT_FILTER
    
- FILTER_SECURITY_INTERCEPTOR
    
- SWITCH_USER_FILTER
    
- LAST
 

<custom-filter>选择FORM_LOGIN_FILTER时机委给Bean-authenticationProcessingFilter使用,这一动作出现频道盖台,<form-login>标签应该失效,委给authenticationProcessingFilter处理。 authenticationProcessingFilter显然是指定org.springframework.security.web.authentication.UsernamePasswordAut​​henticationFilter来处理的,此处在Google上又会得到错误类别名,正名是UsernamePasswordAut​​henticationFilter,而Google的类别名是UsernamePasswordAut​​henticationProcessingFilter,多出了Processing,所以我才猜是不是前版的遗留,至少在3.0.2版没这个类别。

authenticationProcessingFilter要指定三类properties,第一个是指定Authentication Manager。若非这次的需求,SecurityContextHolder会找到<authentication-manager>这个标签作为预设值。但既然委托给Spring Bean处理,所以要有的bean reference的name,所以后来才在<authentication-manager>加个alias属性,这样<authentication-manager>才能被authenticationProcessingFilter找到。

authenticationProcessingFilter要指定的第二类properties是本篇文章目的主程式所在,储存username至session。最多有四种bean,最常用的是认证成功(AuthenticationSuccessHandler)和认证失败(AuthenticationFailureHandler),尚有未认证的访问及已认证访问受保护的URL。而储存至session的动作在第一种,是故AuthenticationSuccessHandler属性指向MyAuthenticationSuccessHandler这个类别,其实作如下:

public class MyAuthenticationSuccessHandler extends
        
SavedRequestAwareAuthenticationSuccessHandler {
    
@Override
    
public void onAuthenticationSuccess(HttpServletRequest request,
            
HttpServletResponse response, Authentication authentication)
            
throws ServletException, IOException {
        
HttpSession session = request.getSession();
        
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        
session.setAttribute("username", userDetails.getUsername());
        
super.onAuthenticationSuccess(request, response, authentication);
    
}
}
 

和前一篇SecurityContextHolder饭粒原理是一样,只是比较文雅,而MyAuthenticationSuccessHandler下设了一个defaultTargetUrl属性,其实也恰恰覆盖<form-login>的default-target-url属性。先前有言,<customer-filter>若设置为CONCURRENT_SESSION_FILTER过不了关,是session值在onAuthenticationSuccess的设定无法带给defaultTargetUrl,所以改成FORM_LOGIN_FILTER就可以work。

第三类是filterProcessesUrl属性,指authenticationProcessingFilter会对哪个URL Pattern产生作用,本例是指/j_spring_security_check,就因为custom-filter设置before="FORM_LOGIN_FILTER"抢在<form-login>拦截,致使<form-login>失效。

以下是成功配置:

<?xml version="1.0" encoding="UTF-8"?>
<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-3.0.xsd"
	default-lazy-init="true">

	<description>SpringSecurity安全配置</description>

	<!-- http安全配置 -->
	<s:http auto-config="true" use-expressions="true">
		<s:intercept-url pattern="/css/**" filters="none" />
		<s:intercept-url pattern="/img/**" filters="none" />
		<s:intercept-url pattern="/js/**" filters="none" />

		<s:intercept-url pattern="/index.jsp" filters="none" />

		<s:intercept-url pattern="/login.action"
			access="hasAnyRole('ROLE_ANONYMOUS')" />
		<s:intercept-url pattern="/logout.jsp"
			access="hasAnyRole('ROLE_ANONYMOUS')" />
		<s:intercept-url pattern="/main/**" access="hasAnyRole('ROLE_通用')" />

		<s:intercept-url pattern="/public*"
			access="hasAnyRole('ROLE_ANONYMOUS')" />

		<s:intercept-url pattern="/public/test.action"
			access="hasAnyRole('ROLE_ANONYMOUS')" />

		<s:intercept-url pattern="/**" access="isAuthenticated()" />

		<s:intercept-url pattern="/account/user!setSession*"
			access="hasAnyRole('ROLE_通用')" />
		<s:intercept-url pattern="/account/user!changepwd*"
			access="hasAnyRole('ROLE_通用')" />

		<s:intercept-url pattern="/account/user!save*" access="hasAnyRole('ROLE_修改用户')" />
		<s:intercept-url pattern="/account/user!delete*"
			access="hasAnyRole('ROLE_修改用户')" />
		<s:intercept-url pattern="/account/user*" access="hasAnyRole('ROLE_浏览用户')" />

		<s:intercept-url pattern="/account/role!save*" access="hasAnyRole('ROLE_修改角色')" />
		<s:intercept-url pattern="/account/role!delete*"
			access="hasAnyRole('ROLE_修改角色')" />
		<s:intercept-url pattern="/account/role*" access="hasAnyRole('ROLE_浏览角色')" />

		<s:intercept-url pattern="/lab/lab!save*" access="hasAnyRole('ROLE_修改实验室')" />
		<s:intercept-url pattern="/lab/lab!delete*" access="hasAnyRole('ROLE_修改实验室')" />
		<s:intercept-url pattern="/lab/lab*" access="hasAnyRole('ROLE_浏览实验室')" />

		<s:form-login login-page="/login.action"
			default-target-url="/main.action" authentication-failure-url="/login.action?error=true" />
		<s:logout logout-success-url="/logout.jsp" />

		<s:custom-filter before="FORM_LOGIN_FILTER" ref="appSessionProcessingFilter" />
	</s:http>



	<!-- 自定义成功和失败处理器,AppSessionSuccessHandler中设置了session -->
	<bean id="appSessionProcessingFilter"
		class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
		<property name="authenticationFailureHandler">
			<bean
				class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
				<property name="defaultFailureUrl" value="/pages/Login/login.do?error=true" />
			</bean>
		</property>
		<property name="authenticationSuccessHandler">
			<bean class="mis.service.account.AppSessionSuccessHandler">
				<property name="defaultTargetUrl" value="/" />
			</bean>
		</property>
		<property name="authenticationManager" ref="authenticationManager"/>
        <property name="filterProcessesUrl" value="/j_spring_security_check"/>
	</bean>


	<!-- 认证配置, 使用userDetailsService提供的用户信息 -->
	<s:authentication-manager alias="authenticationManager">
		<s:authentication-provider user-service-ref="userDetailsService">
			<s:password-encoder hash="plaintext" />
		</s:authentication-provider>
	</s:authentication-manager>

	<!-- 项目实现的用户查询服务 -->
	<bean id="userDetailsService" class="mis.service.account.UserDetailsServiceImpl" />
</beans>

 

package mis.service.account;

import java.io.IOException;

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

import mis.dao.account.UserDao;
import mis.entity.account.User;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;

public class AppSessionSuccessHandler extends
		SavedRequestAwareAuthenticationSuccessHandler {
	@Autowired
    private UserDao userDao;
	@Override 
	public void onAuthenticationSuccess(HttpServletRequest request,HttpServletResponse response,Authentication authentication)throws ServletException,IOException{
		HttpSession session=request.getSession();
		UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        User currentUser = userDao.findUnique("loginname", userDetails.getUsername().toString());
        session.setAttribute("currentUser", currentUser);
        
        System.out.println("do it success");

	super.onAuthenticationSuccess(request,response,authentication);}
	public UserDao getUserDao() {
		return userDao;
	}
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}}

 

 

分享到:
评论
1 楼 macjava85 2011-08-31  
强人,理解的很深刻

相关推荐

Global site tag (gtag.js) - Google Analytics