由于参与公司的一个项目用到了spring security3。开始以为ss3这种安全框架应该不会太难,可看了几天之后发现依旧不能运行出满足需求的demo出来。我去难道是我太笨了。又接着研究了一下终于大体上了解了spring security3 的用法。有不妥之处请各位大神指出。
使用的spring security版本是3.0.8请大家注意一下
项目需求:将权限,资源和角色存储在数据库中。不能再配置文件中采用硬编码的形式配置。
如图所示的物理模型:
角色与用户为1:n的关系
角色与资源为n:n的关系
创建数据库(mysql)
drop table if exists resources;
drop table if exists role_resource;
drop table if exists roles;
drop table if exists users;
/*==============================================================*/
/* Table: resources */
/*==============================================================*/
create table resources
(
rsid int not null auto_increment,
rsurl varchar(50) not null,
primary key (rsid)
);
/*==============================================================*/
/* Table: role_resource */
/*==============================================================*/
create table role_resource
(
rid int not null,
rsid int not null,
primary key (rid, rsid)
);
/*==============================================================*/
/* Table: roles */
/*==============================================================*/
create table roles
(
rid int not null auto_increment,
rname varchar(20) not null,
rdescription text,
primary key (rid)
);
/*==============================================================*/
/* Table: users */
/*==============================================================*/
create table users
(
uid int not null auto_increment,
rid int not null,
uenable varchar(10) not null,
uusername varchar(50) not null,
upassword varchar(50) not null,
uemail varchar(50) not null,
primary key (uid)
);
使用的数据库设计完成。官方提供了两张默认使用的数据库表结构用于在实现UserDetailsService类。用户装载用户名,密码和账户状态。如果使用默认数据库则不用扩展则可以实现
如果不使用默认的表结构则要扩展如下四个类:
AbstractSecurityInterceptor 用于过滤URL。
UserDetailsService 用于装载用户信息。
FilterInvocationSecurityMetadataSource 用于获取URL所对应的权限
AccessDecisionManager 用于判断用户有没有权限访问。
首先让我们继承抽象类 AbstractSecurityInterceptor 代码如下
public class MySecurityInterceptor extends AbstractSecurityInterceptor implements Filter{ private FilterInvocationSecurityMetadataSource securityMetadataSource; public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FilterInvocation filterInvocation = new FilterInvocation(request, response, chain); InterceptorStatusToken interceptorStatusToken = this.beforeInvocation(filterInvocation); filterInvocation.getChain().doFilter(request, response); this.afterInvocation(interceptorStatusToken, null); } public void init(FilterConfig filterConfig) throws ServletException { } @Override public Class<? extends Object> getSecureObjectClass() { return FilterInvocation.class; } @Override public SecurityMetadataSource obtainSecurityMetadataSource() { return this.securityMetadataSource; } public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() { return securityMetadataSource; } public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) { this.securityMetadataSource = securityMetadataSource; } }
实现FilterInvocationSecurityMetadataSource接口
public class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource { private Map<String, Collection<ConfigAttribute>> map; private UrlMatcher urlMatcher = new AntUrlPathMatcher(); public MyInvocationSecurityMetadataSourceService(JdbcTemplate jdbcTemplate){ super(); String sql = "select rname from roles"; List<Map<String, Object>> list = jdbcTemplate.queryForList(sql); ConfigAttribute rolename = null; String urlsql = ""; String url = ""; Iterator<Map<String, Object>> iterator = list.iterator(); map = new HashMap<String, Collection<ConfigAttribute>>(); System.out.println(list.toString()); while (iterator.hasNext()) { rolename = new SecurityConfig((String) iterator.next().get("rname")); urlsql = "select c.rsurl from roles a, role_resources b,resources c where a.rid=b.rid and b.rsid=c.rsid and a.rname=" + "'" + rolename.getAttribute() + "'"; List<Map<String, Object>> list2 = jdbcTemplate.queryForList(urlsql); System.out.println(list2.toString()); Iterator<Map<String, Object>> iterator2 = list2.iterator(); while (iterator2.hasNext()) { url = (String) iterator2.next().get("rsurl"); if (map.containsKey(url)) { Collection<ConfigAttribute> value = map.get(url); value.add(rolename); map.put(url, value); } else { Collection<ConfigAttribute> att = new ArrayList<ConfigAttribute>(); att.add(rolename); map.put(url, att); } } } } public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { String url = ((FilterInvocation) object).getRequestUrl(); int urlQuestionMarkIndex = url.indexOf("?"); if (urlQuestionMarkIndex != -1) { url = url.substring(0, urlQuestionMarkIndex); } Iterator<String> iterator = map.keySet().iterator(); String resurl = ""; while (iterator.hasNext()) { resurl = iterator.next(); if (urlMatcher.pathMatchesUrl(url, resurl)) { System.out.print(",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"); System.out.println(map.get(url).toString()); return map.get(url); } } return null; } public boolean supports(Class<?> arg0) { return true; } }
实现UserDetailsService接口
public class AccessDecisionManager implements org.springframework.security.access.AccessDecisionManager{ public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { if (configAttributes==null) return; Iterator<ConfigAttribute> iterator = configAttributes.iterator(); while(iterator.hasNext()){ ConfigAttribute configAttribute = iterator.next(); String needRole = ((SecurityConfig)configAttribute).getAttribute(); System.out.println("///////////////////////////////////////"); System.out.println(needRole); System.out.println(object.getClass().getName()); for(GrantedAuthority ga : authentication.getAuthorities()){ if(needRole.equals(ga.getAuthority())){ System.out.println(ga.getAuthority()); return ; } } } throw new AccessDeniedException("没有权限"); } public boolean supports(ConfigAttribute arg0) { return true; } public boolean supports(Class<?> arg0) { return true; } }
实现AccessDecisionManager接口
public class AccessDecisionManager implements org.springframework.security.access.AccessDecisionManager{ public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { if (configAttributes==null) return; Iterator<ConfigAttribute> iterator = configAttributes.iterator(); while(iterator.hasNext()){ ConfigAttribute configAttribute = iterator.next(); String needRole = ((SecurityConfig)configAttribute).getAttribute(); System.out.println("///////////////////////////////////////"); System.out.println(needRole); System.out.println(object.getClass().getName()); for(GrantedAuthority ga : authentication.getAuthorities()){ if(needRole.equals(ga.getAuthority())){ System.out.println(ga.getAuthority()); return ; } } } throw new AccessDeniedException("没有权限"); } public boolean supports(ConfigAttribute arg0) { return true; } public boolean supports(Class<?> arg0) { return true; } }
下面是配置文件
<?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="/accessDenied。jsp" use-expressions="true"> <form-login login-page="/login.jsp" /> <logout logout-success-url="/login.jsp" /> <intercept-url pattern="/login.jsp*" filters="none" /> <intercept-url pattern="/**" access="isAuthenticated()" /> <session-management invalid-session-url="/sessionTimeout.jsp"> </session-management> <custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR" /> </http> <authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="userDetailsManager"> </authentication-provider> </authentication-manager> <beans:bean id="userDetailsManager" class="com.xiaoqiang.userdetailsservice.MyUserDetailsService"> <beans:property name="myJdbcTemplate" ref="jdbcTemplate"></beans:property> </beans:bean> <beans:bean id="myFilter" class="com.xiaoqiang.abstractsecurityinterceptor.MySecurityInterceptor"> <beans:property name="authenticationManager" ref="authenticationManager" /> <beans:property name="accessDecisionManager" ref="myAccessDecisionManager" /> <beans:property name="securityMetadataSource" ref="mySecurityMetadataSource" /> </beans:bean> <beans:bean id="myAccessDecisionManager" class="com.xiaoqiang.accessdecisionmanager.AccessDecisionManager"> </beans:bean> <beans:bean id="mySecurityMetadataSource" class="com.xiaoqiang.securitymetadatasource.MyInvocationSecurityMetadataSourceService"> <beans:constructor-arg index="0" ref="jdbcTemplate"></beans:constructor-arg> </beans:bean> <beans:bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <beans:property name="basename" value="classpath:message_zh_CN" /> </beans:bean> <beans:bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <beans:property name="dataSource" ref="dataSource"></beans:property> </beans:bean> <beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <beans:property name="driverClassName" value="com.mysql.jdbc.Driver"></beans:property> <beans:property name="url" value="jdbc:mysql://localhost:3306/test1"></beans:property> <beans:property name="username" value="root"></beans:property> <beans:property name="password" value="ragedream"></beans:property> </beans:bean> </beans:beans>
配置完成