最近学习了一下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的工作做的全面
1444

被折叠的 条评论
为什么被折叠?



