读书人

关于spring security的一个小事例

发布时间: 2012-07-19 16:02:20 作者: rapoo

关于spring security的一个小例子

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

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

?

先贴个项目的结构

???? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 关于spring security的一个小事例

?

与spring相关的一些jar包

?????????????????????????????????????????? 关于spring security的一个小事例

?

这个例子中只对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" 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" ref="authenticationManager" />          <beans:property name="accessDecisionManager" ref="accessDecisionManager" />          <beans:property name="securityMetadataSource" ref="securityMetadataSource" /></beans:bean><!-- 资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问 -->  <beans:bean id="securityMetadataSource" init-method="loadResourceDefine"name="code">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 AbstractSecurityInterceptorimplements 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() {}@Overridepublic SecurityMetadataSource obtainSecurityMetadataSource() {return this.securityMetadataSource;}@Overridepublic 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 implementsFilterInvocationSecurityMetadataSource {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的工作做的全面

关于spring security的一个小事例

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

读书人网 >行业软件

热点推荐