spring security3
由于参与公司的一个项目用到了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>
配置完成