`
com0606
  • 浏览: 60284 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

关于spring security的一个小例子

阅读更多

最近学习了一下spring mvc,顺便就把spring security给看了看。看的官方文档,并借鉴了网上别人的学习经验,看了些源码,大致上对其工作原理有了个了解。把自己弄的做个记录,以后用得着了再翻翻

用的是spring3.0.5,lib里面需要导入的包都移去了

 

先贴个项目的结构

                                          

 

与spring相关的一些jar包

                                          

 

这个例子中只对url的权限进行控制,没有用spring的method权限控制。

感觉spring mvc确实比较轻巧,不像struts2包装了太多的东西。不过在这里并没有对spring mvc过多研究,更多的是关于spring security的。

web.xml里面的配置如下

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
	xmlns="http://java.sun.com/xml/ns/javaee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	
	<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
        	classpath:/applicationContext.xml
        	/WEB-INF/applicationContext-security.xml
        </param-value>
    </context-param>
	
	<!-- Spring Security的 配置-->
	<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>
	
	<!-- 配置UTF-8编码过滤 -->
	<filter>
		<filter-name>Set Character Encoding</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<!-- 进行强制转码 -->
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	
	<!-- Spring MVC的配置 -->
	<!-- 默认所对应的配置文件是WEB-INF下的{servlet-name}-servlet.xml -->
	<servlet>
		<servlet-name>springmvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	
	<servlet-mapping>
		<servlet-name>springmvc</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<listener>
      <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
    </listener>
	
	<!-- 默认的spring配置文件是在WEB-INF下的applicationContext.xml-->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener> 
	
	<!-- spring security监听session -->
	<listener>
		<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
	</listener>
	
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
</web-app>

 springmvc-servlet.xml

此文件名根据web.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:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation=" 
           http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-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/mvc 
           http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd" 
           default-autowire="byName">
    
    <!-- 扫描所有的controller -->
    <context:component-scan base-package="cn.henu.springmvc.control" />
    
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    	<property name="prefix" value="/"></property>
    	<property name="suffix" value=".jsp"></property>
    </bean>
    
</beans>  

applicationContext-security.xml

这是专门用来配置spring security的,这里没有用默认的拦截器链,并使用了一个自定义的Filter。使用自定义的Filter需要继承AbstractSecurityInterceptor,并实现两个接口FilterInvocationSecurityMetadataSource,

AccessDecisionManager。如果想使用自定义的用户数据源还需要实现UserDetailsService接口

<?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 auto-config="true" access-denied-page="/error.jsp">
		
		<!-- 不过滤图片等静态资源,其中**代表可以跨越目录,*不可以跨越目录-->
		<intercept-url pattern="/image/**" filters="none" />
		
		<form-login login-page="/login.jsp" login-processing-url="/login"
			authentication-success-handler-ref="authenticationDispatcher" 
			authentication-failure-url="/login.jsp?error=-1" />
		<!-- login-processing-url默认是/j_spring_security_check -->

		<logout logout-url="/logout" logout-success-url="/" />
		<!-- 此配置debug时会报logout-success-url项路径未找到的错,可无视之 -->
			
		<session-management session-fixation-protection="none">  
			<concurrency-control max-sessions="1" expired-url="/login.jsp"/>  
		</session-management>
		<custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR" /> 
	</http>
	
	<!-- 一个自定义的Filter,必须包含以下三项配置 -->
	<beans:bean id="myFilter" class="cn.henu.spring.security.FilterSecurityInterceptor">
		<beans:property name="authenticationManager" ref="authenticationManager" />  
        <beans:property name="accessDecisionManager" ref="accessDecisionManager" />  
        <beans:property name="securityMetadataSource" ref="securityMetadataSource" />
	</beans:bean>
	
	<!-- 资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问 -->  
	<beans:bean id="securityMetadataSource" init-method="loadResourceDefine"
		class="cn.henu.spring.security.InvocationSecurityMetadataSourceService">
		<beans:property name="interceptUrl">
			<beans:props>
				<beans:prop key="/admin/**">admin</beans:prop>
				<beans:prop key="/user/**">user,admin</beans:prop>
			</beans:props>
		</beans:property>
	</beans:bean>
	
	<!-- 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源     -->  
	<beans:bean id="accessDecisionManager" 
		class="cn.henu.spring.security.AccessDecisionSecurityManager">
	</beans:bean>
	
	<authentication-manager alias="authenticationManager">
		<authentication-provider user-service-ref="userDetailService">
		</authentication-provider>
	</authentication-manager>
	
	<!-- 查询用户信息 -->
	<beans:bean id="userDetailService" class="cn.henu.spring.security.UserDetailService">
	</beans:bean>
	
	<!-- 自定义一个角色控制器,根据不同角色登录到不同页面 -->
	<beans:bean id="authenticationDispatcher" class="cn.henu.spring.security.SuccessHandler">
		<beans:property name="authDispatcherMap">
			<beans:props>
				<beans:prop key="admin">/admin/index.jsp</beans:prop>
				<beans:prop key="user">/user/index.jsp</beans:prop>
			</beans:props>
		</beans:property>
	</beans:bean>
</beans:beans>

以下是三个实现类:

FilterSecurityInterceptor.java

package cn.henu.spring.security;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.log4j.Logger;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;

public class FilterSecurityInterceptor extends AbstractSecurityInterceptor
	implements Filter {
	private static final Logger logger = Logger.getLogger(FilterSecurityInterceptor.class);
	private FilterInvocationSecurityMetadataSource securityMetadataSource;	
	public void init(FilterConfig arg0) throws ServletException {
	}
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		if(logger.isInfoEnabled()) {
			logger.info("doFilter~start");
		}
		FilterInvocation invocation = new FilterInvocation(request, response, chain);
		invoke(invocation);
		if(logger.isInfoEnabled()) {
			logger.info("doFilter~end");
		}
	}
	
	public void invoke(FilterInvocation invocation)throws IOException, ServletException {
		InterceptorStatusToken token = super.beforeInvocation(invocation);
		
		try {
			invocation.getChain().doFilter(invocation.getRequest(), invocation.getResponse());
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			super.afterInvocation(token, null);
		}
	}
	
	public void destroy() {
	}
	
	@Override
	public SecurityMetadataSource obtainSecurityMetadataSource() {
		return this.securityMetadataSource;
	}
	
	@Override
	public Class<? extends Object> getSecureObjectClass() {
		return FilterInvocation.class;
	}
	public void setSecurityMetadataSource(
			FilterInvocationSecurityMetadataSource securityMetadataSource) {
		this.securityMetadataSource = securityMetadataSource;
	}
}

 InvocationSecurityMetadataSourceService.java

package cn.henu.spring.security;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.AntUrlPathMatcher;
import org.springframework.security.web.util.UrlMatcher;

public class InvocationSecurityMetadataSourceService implements
		FilterInvocationSecurityMetadataSource {
	private static final Logger logger = 
		Logger.getLogger(InvocationSecurityMetadataSourceService.class);
	private UrlMatcher urlMatcher = new AntUrlPathMatcher();
	private static Map<String, Collection<ConfigAttribute>> resourceMap;
	//用以接收配置文件中的对应关系,类似于http配置项中的<intercept-url />
	private Map<String, String> interceptUrl;

	@SuppressWarnings("unused")
	private void loadResourceDefine() {
		
		resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
		if(interceptUrl==null) {
			if(logger.isInfoEnabled()) {
				logger.info("未建立任何对应关系");
			}
			return;
		}
		//从配置文件中读取对应关系
		for(Map.Entry<String, String> entry : interceptUrl.entrySet()) {
			Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>(); 
	        ConfigAttribute ca = null;
	        if(entry.getValue()==null) {
	        	continue;
	        }
	        for(String configAttribute : entry.getValue().split(",")) {
	        	ca = new SecurityConfig(configAttribute);
	        	atts.add(ca);
	        }
	        resourceMap.put(entry.getKey(), atts);
		}
		
		/*
		//通过硬编码设置,resouce和role
        Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>(); 
        ConfigAttribute ca = new SecurityConfig("admin");
        atts.add(ca);
        resourceMap.put("/admin/**", atts);
        ca = new SecurityConfig("user");
        atts = new ArrayList<ConfigAttribute>();
        atts.add(ca);
        resourceMap.put("/user/**", atts);
        */
		
		if(logger.isInfoEnabled()) {
			logger.info("资源和权限对应关系读取完成!");
		}
	}
	
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		return null;
	}

	public Collection<ConfigAttribute> getAttributes(Object object)
			throws IllegalArgumentException {
		String resUrl = ((FilterInvocation) object).getRequestUrl();
		Iterator<String> iterator = resourceMap.keySet().iterator();
		while (iterator.hasNext()) {
			String url = iterator.next();
			
			if(urlMatcher.pathMatchesUrl(url, resUrl)) {
				Collection<ConfigAttribute> col = resourceMap.get(url);
				if(logger.isInfoEnabled()) {
					logger.info("请求的url与资源中的url相匹配.");
				}
				return col;
			}
		}
		return null;
	}

	public boolean supports(Class<?> clazz) {
		return true;
	}

	public void setInterceptUrl(Map<String, String> interceptUrl) {
		this.interceptUrl = interceptUrl;
	}
}

 AccessDecisionSecurityManager.java

package cn.henu.spring.security;

import java.util.Collection;
import java.util.Iterator;

import org.apache.log4j.Logger;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;

public class AccessDecisionSecurityManager implements AccessDecisionManager {
	private static final Logger logger = Logger.getLogger(AccessDecisionManager.class);
	
	public void decide(Authentication authentication, Object object,
			Collection<ConfigAttribute> collection) throws AccessDeniedException,
			InsufficientAuthenticationException {
		if(logger.isInfoEnabled()) {
			logger.debug("decide(Authentication, Object," + 
					"Collection<ConfigAttribute>)~start");
		}
		if(collection==null) {
			if(logger.isInfoEnabled()) {
				logger.info("decide(Authentication, Object," + 
						"Collection<ConfigAttribute>)~end");
			}	
			return;
		}
		if(logger.isInfoEnabled()) {
			logger.info("正在访问的url是: "+object.toString());
		}
		Iterator<ConfigAttribute> iterator = collection.iterator();
		
		while (iterator.hasNext()) {
			ConfigAttribute ca = iterator.next();
			String needRole = ((SecurityConfig) ca).getAttribute();
			
			for (GrantedAuthority ga : authentication.getAuthorities()) {
				if(needRole.equals(ga.getAuthority())) {
					if(logger.isInfoEnabled()) {
						logger.info("用户角色" + needRole+" 与 "+
								"权限" + ga.getAuthority()+" 相匹配.");
						logger.info("decide(Authentication, Object," + 
							"Collection<ConfigAttribute>)~end");
					}
					return;
				}
			}
		}
		throw new AccessDeniedException("没有权限");
	}

	public boolean supports(ConfigAttribute arg0) {
		return true;
	}

	public boolean supports(Class<?> arg0) {
		return true;
	}

}

 使用自定义数据源

UserDetailService.java

package cn.henu.spring.security;

import java.util.ArrayList;
import java.util.Collection;

import javax.annotation.Resource;

import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
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 cn.henu.service.UserService;

public class UserDetailService implements UserDetailsService {
	
	@Resource private UserService userService;
	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException, DataAccessException {
		Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
		String password = null;
		cn.henu.model.User newUser = userService.getObject(username);
		if(newUser==null) {
			throw new UsernameNotFoundException("用户 "+username+" 不存在");
		}
		password = newUser.getPassword();
		GrantedAuthorityImpl grantedAuthorityImpl = 
			new GrantedAuthorityImpl(newUser.getRole());
		auths.add(grantedAuthorityImpl);

                /*
		 * 此处若用继承自UserDetails的User需要覆写其中的equals和hashCode方法,
		 * 否则无法通过session-management控制max-sessions="1"
		 */
		return new User(username, password, true, true, true, true, auths);
	}

}

 如果希望不同用户登录成功之后进入不同页面,可以实现AuthenticationSuccessHandler接口,并在<form-login />中配置authentication-success-handler-ref项

SuccessHandler.java

package cn.henu.spring.security;

import java.io.IOException;
import java.util.Collection;
import java.util.Map;

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

import org.apache.log4j.Logger;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.util.Assert;

public class SuccessHandler implements AuthenticationSuccessHandler {
	private Map<String, String> map;
	private static final Logger logger = Logger.getLogger(SuccessHandler.class);
	public void onAuthenticationSuccess(HttpServletRequest request,
			HttpServletResponse response, Authentication authentication) throws IOException,
			ServletException {
		Assert.notNull(map, "AuthInterceptMap is null!");
		String url = "";
		Collection<GrantedAuthority> autCollection = authentication.getAuthorities();
		
		if(autCollection.isEmpty()) {
			return;
		}
		GrantedAuthority[] ga = new GrantedAuthorityImpl[]{}; 
		url = map.get(autCollection.toArray(ga)[0].toString());		//取第一角色权限
		
		if(logger.isInfoEnabled()) {
			logger.info("登陆成功,跳转至"+url);
		}
		response.sendRedirect(request.getContextPath() + url);
	}

	public void setAuthDispatcherMap(Map<String, String> map) {
		this.map = map;
	}
}

 

 

关于hibernate的一些配置和spring mvc相关的文件就不贴出来了。

其实spring security中一些对于权限的管理自己写一个过滤器完全可以实现,但是毕竟不如spring security的工作做的全面

4
0
分享到:
评论
3 楼 laowoof 2012-05-30  
为啥我看不懂
2 楼 mojunbin 2011-11-22  
感觉还可以
1 楼 guxinghanshe 2011-09-25  
很不错的例子,一步一步来,基本功能已经实现

相关推荐

Global site tag (gtag.js) - Google Analytics