Spring security2 例子
最近拿出几天看了下Spring Security2,Spring Security是从Acige发展而来,网上和基本参考书上找到的例子大多是Acige的,Spring Security的不多。
1.准备
必须先理顺以下几个Spring Security核心类的作用和关系
- Authentication :认证信息
- AuthenticationManager 认证管理器
- AuthenticationProvider 验证数据提供器
- UserDetailsService:用户信息服务接口
- AccessDecisionManager 授权器
常用过滤器
AuthenticationProcessingFilter 处理form登陆的过滤器,与form登陆有关的所有操作都是在此进行的。
LogoutFilter 只处理注销请求,默认为/j_spring_security_logout。
FilterSecurityInterceptor URL拦截过滤器
2.实体类
RBAC 模型 ,一共五张表:用户、用户角色、角色、角色权限、权限,本例使用Hibernate映射。
三个实体类:User、Role、Resource。
User 继承UserDetails接口,并实现到Role的单向多对多映射
publicclass User implements UserDetails{
privatestaticfinallong serialVersionUID =-1689430377678136883L;
private Integer id;
private String name;
private String password;
private Integer disabled;
private Set<Role> roles =new HashSet<Role>(0);
//单向多对多映射,用户登陆验证时可以获得权限,非延迟加载
@Override
public GrantedAuthority[] getAuthorities() { //获得用户的角色列表,GrantedAuthorityImpl存储的是Role名称
List<GrantedAuthority> grantedAuthorities =new ArrayList<GrantedAuthority>(roles.size());
for(Role role : this.roles) {
grantedAuthorities.add(new GrantedAuthorityImpl(role.getName()));
}
return grantedAuthorities.toArray(new GrantedAuthority[roles.size()]);
}
privatestaticfinallong serialVersionUID =-1689430377678136883L;
private Integer id;
private String name;
private String password;
private Integer disabled;
private Set<Role> roles =new HashSet<Role>(0);
//单向多对多映射,用户登陆验证时可以获得权限,非延迟加载
@Override
public GrantedAuthority[] getAuthorities() { //获得用户的角色列表,GrantedAuthorityImpl存储的是Role名称
List<GrantedAuthority> grantedAuthorities =new ArrayList<GrantedAuthority>(roles.size());
for(Role role : this.roles) {
grantedAuthorities.add(new GrantedAuthorityImpl(role.getName()));
}
return grantedAuthorities.toArray(new GrantedAuthority[roles.size()]);
}
Role比较简单,POJO类
publicclass Role implements java.io.Serializable {
privatestaticfinallong serialVersionUID =-953612771016471024L;
private Integer id;
private String name;
}
privatestaticfinallong serialVersionUID =-953612771016471024L;
private Integer id;
private String name;
}
Resouce类需要单向关联Role
publicclass Resource implements java.io.Serializable {
privatestaticfinallong serialVersionUID =5604673911728289859L;
private Integer id;
private String type;//资源类型,例如URL
private String value;
private Set<Role> roles =new HashSet<Role>(0);//Hibernate配置
}
privatestaticfinallong serialVersionUID =5604673911728289859L;
private Integer id;
private String type;//资源类型,例如URL
private String value;
private Set<Role> roles =new HashSet<Role>(0);//Hibernate配置
}
3.DAO层代码
两个DAO类,UserDao和ResourceDao
UserDao类
publicinterface UserDao {
public User loadUserByUsername(String username);//通过用户名获得User
}
public User loadUserByUsername(String username);//通过用户名获得User
}
ResourceDao类
publicinterface ResourceDao {
public List<Resource> loadResourceByType(String type);
//根据类型获得Resource队列,例如获得URL资源 loadResourceByType("URL")
}
public List<Resource> loadResourceByType(String type);
//根据类型获得Resource队列,例如获得URL资源 loadResourceByType("URL")
}
4.Security Support支持类
UserDetailsServiceImpl继承org.springframework.security.userdetails.UserDetails接口,只需要实现一个方法
publicclass UserDetailsServiceImpl implements UserDetailsService {
private UserDao userDao;//spring注入
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
return userDao.loadUserByUsername(username);
}//没有用户时抛出UsernameNotFoundException异常
private UserDao userDao;//spring注入
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
return userDao.loadUserByUsername(username);
}//没有用户时抛出UsernameNotFoundException异常
publicvoid setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
this.userDao = userDao;
}
}
URLDefinitionSourceFactory的目的是构造一个DefaultFilterInvocationDefinitionSource类
publicclass URLDefinitionSourceFactory implements FactoryBean{
/**
* 这个类的目的就是构建一个DefaultFilterInvocationDefinitionSource类,
* DefaultFilterInvocationDefinitionSource是默认提供的FilterInvocationDefinitionSource实现类,省力
* 本类继承FactoryBean给DefaultFilterInvocationDefinitionSource构造的两个参数赋值
* 关于FactoryBean接口的使用,Spring新手自己查下
*/
private ResourceDao resourceDao;//Spring注入
//返回DefaultFilterInvocationDefinitionSource构造参数之一
private UrlMatcher getUrlMatcher() {
returnnew AntUrlPathMatcher();//这个比RegexUrlPathMatcher简单,所以用这个,不求甚解
}
//返回DefaultFilterInvocationDefinitionSource构造参数之二
private LinkedHashMap<RequestKey, ConfigAttributeDefinition> getRequestMap() throws Exception {
List<Resource> resources= resourceDao.loadResourceByType("URL");//取所有URL资源
LinkedHashMap<RequestKey, ConfigAttributeDefinition> requestMap =new LinkedHashMap<RequestKey,ConfigAttributeDefinition>();
//这个Map看起来比较唬人,把握两点:key为URL地址,value为允许访问该URL的角色S,URL与ROLE是多对多的关系
for (Resource resource : resources) {
Set<Role> roles = resource.getRoles();
int i = roles.size();
String[] rolenames =new String[i];//将该URL的的角色名称转化为一个String数组
for (Role role : roles) {
i--;
rolenames[i] = role.getName();
}
requestMap.put(new RequestKey(resource.getValue()), new ConfigAttributeDefinition(rolenames));
//key为URL地址,用RequestKey封装
//value为ConfigAttributeDefinition,可以理解为一个数组,该数组中存放ConfigAttribute
//每个ConfigAttribute封装一个角色名称
}
return requestMap;
}
@Override
public Object getObject() throws Exception {//工厂方法
LinkedHashMap<RequestKey, ConfigAttributeDefinition> requestMap = getRequestMap();//取参数一
UrlMatcher matcher = getUrlMatcher(); //取参数二
DefaultFilterInvocationDefinitionSource definitionSource =new DefaultFilterInvocationDefinitionSource(
matcher, requestMap); //构造完成
return definitionSource;
}
@Override
public Class<DefaultFilterInvocationDefinitionSource> getObjectType() {//FactoryBean接口方法
return DefaultFilterInvocationDefinitionSource.class;
}
@Override
publicboolean isSingleton() {//FactoryBean接口方法
returntrue;
}
publicvoid setResourceDao(ResourceDao resourceDao) {
this.resourceDao = resourceDao;
}
}
/**
* 这个类的目的就是构建一个DefaultFilterInvocationDefinitionSource类,
* DefaultFilterInvocationDefinitionSource是默认提供的FilterInvocationDefinitionSource实现类,省力
* 本类继承FactoryBean给DefaultFilterInvocationDefinitionSource构造的两个参数赋值
* 关于FactoryBean接口的使用,Spring新手自己查下
*/
private ResourceDao resourceDao;//Spring注入
//返回DefaultFilterInvocationDefinitionSource构造参数之一
private UrlMatcher getUrlMatcher() {
returnnew AntUrlPathMatcher();//这个比RegexUrlPathMatcher简单,所以用这个,不求甚解
}
//返回DefaultFilterInvocationDefinitionSource构造参数之二
private LinkedHashMap<RequestKey, ConfigAttributeDefinition> getRequestMap() throws Exception {
List<Resource> resources= resourceDao.loadResourceByType("URL");//取所有URL资源
LinkedHashMap<RequestKey, ConfigAttributeDefinition> requestMap =new LinkedHashMap<RequestKey,ConfigAttributeDefinition>();
//这个Map看起来比较唬人,把握两点:key为URL地址,value为允许访问该URL的角色S,URL与ROLE是多对多的关系
for (Resource resource : resources) {
Set<Role> roles = resource.getRoles();
int i = roles.size();
String[] rolenames =new String[i];//将该URL的的角色名称转化为一个String数组
for (Role role : roles) {
i--;
rolenames[i] = role.getName();
}
requestMap.put(new RequestKey(resource.getValue()), new ConfigAttributeDefinition(rolenames));
//key为URL地址,用RequestKey封装
//value为ConfigAttributeDefinition,可以理解为一个数组,该数组中存放ConfigAttribute
//每个ConfigAttribute封装一个角色名称
}
return requestMap;
}
@Override
public Object getObject() throws Exception {//工厂方法
LinkedHashMap<RequestKey, ConfigAttributeDefinition> requestMap = getRequestMap();//取参数一
UrlMatcher matcher = getUrlMatcher(); //取参数二
DefaultFilterInvocationDefinitionSource definitionSource =new DefaultFilterInvocationDefinitionSource(
matcher, requestMap); //构造完成
return definitionSource;
}
@Override
public Class<DefaultFilterInvocationDefinitionSource> getObjectType() {//FactoryBean接口方法
return DefaultFilterInvocationDefinitionSource.class;
}
@Override
publicboolean isSingleton() {//FactoryBean接口方法
returntrue;
}
publicvoid setResourceDao(ResourceDao resourceDao) {
this.resourceDao = resourceDao;
}
}
5.applicationContext-security.xml配置文件
<?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-2.5.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.6.xsd">
<!-- 支持类之一 userDatailsService接口实现-->
<beans:bean id="userDatailsService" class="com.kaqike.security.security.support.UserDetailsServiceImpl">
<beans:property name="userDao" ref="userDao"/>
</beans:bean>
<!-- 支持类之二 DefaultFilterInvocationDefinitionSource工厂类-->
<beans:bean id="URLDefinitionSourceFactory" class="com.kaqike.security.security.URLDefinitionSourceFactory">
<beans:property name="resourceDao" ref="resourceDao"/>
</beans:bean>
<!--http 是Spring Security的关键配置,各个拦截器在此实现 -->
<http auto-config='true'>
<concurrent-session-control max-sessions="1" exception-if-maximum-exceeded="true"/>
</http>
<!-- userDatailsService配置,使用md5验证 -->
<authentication-provider user-service-ref="userDatailsService">
<password-encoder hash="md5"></password-encoder>
</authentication-provider>
<authentication-manager alias="authenticationManager"/>
<!--授权器 -->
<beans:bean id="accessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
<beans:property name="allowIfAllAbstainDecisions" value="false"/>
<beans:property name="decisionVoters">
<beans:list>
<beans:bean class="org.springframework.security.vote.RoleVoter"/>
<beans:bean class="org.springframework.security.vote.AuthenticatedVoter"/>
</beans:list>
</beans:property>
</beans:bean>
<!--URL资源拦截器 -->
<beans:bean id="URLSecurityInterceptor" class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="accessDecisionManager" ref="accessDecisionManager"/>
<beans:property name="objectDefinitionSource" ref="URLDefinitionSourceFactory"/>
<beans:property name="observeOncePerRequest" value="false"/>
<custom-filter after="LAST"/>
</beans:bean>
<!-- 登录成功监听器 -->
<beans:bean class="com.kaqike.security.listener.LoginSuccessListener"></beans:bean>
<!-- 认证日志监听器 -->
<beans:bean id="loggerListener" class="org.springframework.security.event.authentication.LoggerListener"/>
</beans:beans>
<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-2.5.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.6.xsd">
<!-- 支持类之一 userDatailsService接口实现-->
<beans:bean id="userDatailsService" class="com.kaqike.security.security.support.UserDetailsServiceImpl">
<beans:property name="userDao" ref="userDao"/>
</beans:bean>
<!-- 支持类之二 DefaultFilterInvocationDefinitionSource工厂类-->
<beans:bean id="URLDefinitionSourceFactory" class="com.kaqike.security.security.URLDefinitionSourceFactory">
<beans:property name="resourceDao" ref="resourceDao"/>
</beans:bean>
<!--http 是Spring Security的关键配置,各个拦截器在此实现 -->
<http auto-config='true'>
<concurrent-session-control max-sessions="1" exception-if-maximum-exceeded="true"/>
</http>
<!-- userDatailsService配置,使用md5验证 -->
<authentication-provider user-service-ref="userDatailsService">
<password-encoder hash="md5"></password-encoder>
</authentication-provider>
<authentication-manager alias="authenticationManager"/>
<!--授权器 -->
<beans:bean id="accessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
<beans:property name="allowIfAllAbstainDecisions" value="false"/>
<beans:property name="decisionVoters">
<beans:list>
<beans:bean class="org.springframework.security.vote.RoleVoter"/>
<beans:bean class="org.springframework.security.vote.AuthenticatedVoter"/>
</beans:list>
</beans:property>
</beans:bean>
<!--URL资源拦截器 -->
<beans:bean id="URLSecurityInterceptor" class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="accessDecisionManager" ref="accessDecisionManager"/>
<beans:property name="objectDefinitionSource" ref="URLDefinitionSourceFactory"/>
<beans:property name="observeOncePerRequest" value="false"/>
<custom-filter after="LAST"/>
</beans:bean>
<!-- 登录成功监听器 -->
<beans:bean class="com.kaqike.security.listener.LoginSuccessListener"></beans:bean>
<!-- 认证日志监听器 -->
<beans:bean id="loggerListener" class="org.springframework.security.event.authentication.LoggerListener"/>
</beans:beans>
6.其他
pom.xml文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>kaqike</groupId>
<artifactId>spring_security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring_security</name>
<description>first spring security app</description>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>${spring.version}</version>
<type>jar</type>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
<version>${spring.security.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring.security.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>${hibernate.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.16</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
</dependencies>
<properties>
<spring.version>2.5.6</spring.version>
<spring.security.version>2.0.6.RELEASE</spring.security.version>
<hibernate.version>3.2.5.GA</hibernate.version>
</properties>
</project>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>kaqike</groupId>
<artifactId>spring_security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring_security</name>
<description>first spring security app</description>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>${spring.version}</version>
<type>jar</type>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
<version>${spring.security.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring.security.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>${hibernate.version}</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.16</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
</dependencies>
<properties>
<spring.version>2.5.6</spring.version>
<spring.security.version>2.0.6.RELEASE</spring.security.version>
<hibernate.version>3.2.5.GA</hibernate.version>
</properties>
</project>