spring security2配置
下面的内容也是在网上查找的资料基础上增加了一下修改,这里也省去了ssh整合的配置,只是针对spring security 2的相关内容
1、首先在web.xml中进行配置
<!-- spring security configuration 这个Filter会拦截所有的URL请求,并且对这些URL请求进行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>
和普通的filter配置一样,非常方便
2、spring-security.xml的配置内容如下:
<b:beans xmlns="http://www.springframework.org/schema/security" xmlns:b="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.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">
<b:description>spring security configuration</b:description>
<!-- Spring Security采用就近原则,有多个约束时,从上至下只要找到第一条满足就返回,因此应该将最严格的约束放在最前面,
而将最宽松的约束放在最后面.auto-config属性可以让spring security为我们自动配置几种常用的权限控制机制,
包括form,anonymous, rememberMe等。当然你也可以手工配置。-->
<http access-denied-page="/access-denied.jsp">
<!-- 我们利用intercept-url来判断用户需要具有何种权限才能访问对应的url资源,可以在pattern中指定一个特定的url资源,
也可以使用通配符指定一组类似的url资源。例子中定义的两个intercepter-url,第一个用来控制对/security/**的访问,
第二个使用了通配符/**,说明它将控制对系统中所有url资源的访问。 -->
<intercept-url pattern="/login.jsp" filters="none" />
<intercept-url pattern="/b.jsp" access="ROLE_ADMIN" />
<intercept-url pattern="/d.jsp" filters="none" />
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp" always-use-default-target="true" default-target-url="/b.jsp"/>
<logout logout-success-url="/login.jsp"/>
</http>
<!-- 验证数据提供器 -->
<authentication-provider user-service-ref='userDetailsService' >
<!-- 将 userDetailsService 的属性username用md5加密
<password-encoder hash="md5">
<salt-source user-property="username"/>
</password-encoder>
-->
</authentication-provider>
<!-- 鉴权过滤器(URL资源拦截器) -->
<b:bean id="filterSecurityInterceptor" /><!-- 指定过滤器应该出现的位置和顺序 -->
<b:property name="authenticationManager" ref="authenticationManager" />
<b:property name="accessDecisionManager" ref="accessDecisionManager" />
<b:property name="objectDefinitionSource" ref="filterInvocationDefinitionSource" />
</b:bean>
<!-- 用户-权限认证管理器 -->
<b:bean id="authenticationManager" />
</b:list>
</b:property>
</b:bean>
<!-- 决策管理器(授权器) -->
<b:bean id="accessDecisionManager" value="" />
</b:bean>
</b:list>
</b:property>
</b:bean>
<!-- 用户认证服务 -->
<b:bean id="daoAuthenticationProvider" ref="userDetailsService" />
<b:property name="hideUserNotFoundExceptions" value="false" />
</b:bean>
<!-- 用户信息服务接口 -->
<b:bean id="userDetailsService" ref="dataSource" />
<b:property name="usersByUsernameQuery">
<b:value><![CDATA[
select u.id,
u.username,
u.password,
u.enable as enabled
from
users u
where u.username = ?
]]></b:value>
</b:property>
<b:property name="authoritiesByUsernameQuery">
<b:value><![CDATA[
select
u.username, r.name as authority
from
users u
join users_roles ur
on u.id = ur.uid
join roles r
on r.id = ur.rid
where
u.username = ?
]]>
</b:value>
</b:property>
</b:bean>
<!-- filter Invocation DefinitionSource -->
<b:bean id="filterInvocationDefinitionSource" ref="dataSource" />
<b:property name="resourceQuery">
<b:value>
<![CDATA[
select
re.url,r.name
from
roles r
join roles_resources rr
on r.id=rr.rid
join resources re
on re.id=rr.rsid
]]>
</b:value>
</b:property>
</b:bean>
</b:beans>
这里我并没有直接采用hibernate的持久化对象,而是使用的JDBC,个人觉得这样更方便,毕竟登录账户数据不会在前台就行操作
3、我把要用用到的实体类及数据库中创建的sql都放在了附件中,都比较简单,这里就不贴出来的了
4、下面是配置文件中要用到的service,各个类的作用在上面的配置文件中都有描述,这里就不再赘述
a、用户登录权限校验类
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.userdetails.UsernameNotFoundException;
public class UserDetailsServiceImpl extends JdbcDaoSupport implements UserDetailsService {
private String usersByUsernameQuery;
private String authoritiesByUsernameQuery;
/**
* 用户查询服务
*/
@SuppressWarnings("unchecked")
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
List<Map<String, Object>> result = super.getJdbcTemplate().queryForList(usersByUsernameQuery, new Object[] { username });
if (result.size() == 1) {
Map<String, Object> row = result.get(0);
row.put(UserDetails.AUTHORITIES, obtainGrantedAuthorities(username));
UserDetails user = new UserDetails();
user.getUserInfo().putAll(row);
return user;
}
throw new UsernameNotFoundException("登陆的账号[" + username + "] 无效!");
}
/**
* 查询用户的角色
*/
@SuppressWarnings("unchecked")
private GrantedAuthority[] obtainGrantedAuthorities(String username) {
List<Map<String, Object>> result = super.getJdbcTemplate().queryForList(this.authoritiesByUsernameQuery, new Object[] { username });
List<GrantedAuthority> grantedAuthoritiesList = new ArrayList<GrantedAuthority>();
for (Map<String, Object> role : result) {
grantedAuthoritiesList.add(new GrantedAuthorityImpl((String) role.get("AUTHORITY")));
}
return (GrantedAuthority[]) grantedAuthoritiesList.toArray(new GrantedAuthority[grantedAuthoritiesList.size()]);
}
/**
* setter
*
* @param usersByUsernameQuery
* sql
*
*/
public void setUsersByUsernameQuery(String usersByUsernameQuery) {
this.usersByUsernameQuery = usersByUsernameQuery.toUpperCase();
}
/**
* setter
*
* @param authoritiesByUsernameQuery
* sql
*/
public void setAuthoritiesByUsernameQuery(String authoritiesByUsernameQuery) {
this.authoritiesByUsernameQuery = authoritiesByUsernameQuery.toUpperCase();
}
}
b、下面这个类用于保存登录账户的相关信息
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.springframework.security.GrantedAuthority;
public class UserDetails implements org.springframework.security.userdetails.UserDetails, Serializable {
public static final long serialVersionUID = -3072292482513179894L;
public static final String USER_NAME = "USERNAME";
public static final String PASSWORD = "PASSWORD";
public static final String AUTHORITIES = "AUTHORITIES";
public static final String ENABLED = "ENABLED";
private Map<String, Object> userInfo = new HashMap<String, Object>();
public Map<String, Object> getUserInfo() {
return userInfo;
}
public void setUserInfo(Map<String, Object> userInfo) {
this.userInfo = userInfo;
}
public GrantedAuthority[] getAuthorities() {
return (GrantedAuthority[]) userInfo.get(AUTHORITIES);
}
public String getPassword() {
return (String) userInfo.get(PASSWORD);
}
public String getUsername() {
return (String) userInfo.get(USER_NAME);
}
public boolean isAccountNonExpired() {
return true;
}
public boolean isAccountNonLocked() {
return true;
}
public boolean isCredentialsNonExpired() {
return true;
}
public boolean isEnabled() {
Object enabled = userInfo.get(ENABLED);
if (enabled instanceof Number) {
enabled = String.valueOf(((Number) enabled).intValue());
}
if (enabled instanceof String) {
if (enabled.equals("1") || ((String) enabled).equalsIgnoreCase("true")) {
return true;
}
return false;
}
return false;
}
}
c、用户访问资源权限校验类
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.jdbc.object.MappingSqlQuery;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.ConfigAttributeEditor;
import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;
import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;
import org.springframework.security.intercept.web.RequestKey;
import org.springframework.security.util.AntUrlPathMatcher;
import org.springframework.security.util.UrlMatcher;
/**
* spring security jdbc FactoryBean
* @version 1.0
* /**
* 这个类的目的就是构建一个DefaultFilterInvocationDefinitionSource类,
* DefaultFilterInvocationDefinitionSource是默认提供的FilterInvocationDefinitionSource实现类,省力
* 本类继承FactoryBean给DefaultFilterInvocationDefinitionSource构造的两个参数赋值
*/
public class JdbcFilterInvocationDefinitionSourceFactoryBean extends JdbcDaoSupport implements FactoryBean {
private String resourceQuery;
/**
* (non-Javadoc)
*
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
public boolean isSingleton() {//FactoryBean接口方法
return true;
}
/**
* (non-Javadoc)
*
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
public Class<?> getObjectType() {//FactoryBean接口方法
return FilterInvocationDefinitionSource.class;
}
/**
* (non-Javadoc)
*
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
public Object getObject() {//工厂方法
//取构造参数一、二
return new DefaultFilterInvocationDefinitionSource(this.getUrlMatcher(), this.buildRequestMap());
}
/**
* 资源-角色的对应关系
*
* @return
*/
@SuppressWarnings("unchecked")
protected Map<String, String> mappingResource() {
Map<String, String> resourceMap = null;
resourceMap = new LinkedHashMap<String, String>();
ResourceMapping resourceMapping = new ResourceMapping(getDataSource(), resourceQuery);
for (Resource resource : (List<Resource>) resourceMapping.execute()) {
String url = resource.getUrl();
String role = resource.getRole();
if (resourceMap.containsKey(url)) {
String value = resourceMap.get(url);
resourceMap.put(url, value + "," + role);
} else {
resourceMap.put(url, role);
}
}
return resourceMap;
}
/**
* buildRequestMap
* 返回DefaultFilterInvocationDefinitionSource构造参数之二
*/
protected LinkedHashMap<RequestKey, ConfigAttributeDefinition> buildRequestMap() {
LinkedHashMap<RequestKey, ConfigAttributeDefinition> requestMap = null;
//这个Map看起来比较唬人,把握两点:key为URL地址,value为允许访问该URL的角色S,URL与ROLE是多对多的关系
requestMap = new LinkedHashMap<RequestKey, ConfigAttributeDefinition>();
ConfigAttributeEditor editor = new ConfigAttributeEditor();
Map<String, String> resourceMap = this.mappingResource();
for (Map.Entry<String, String> entry : resourceMap.entrySet()) {
//key为URL地址,用RequestKey封装
//value为ConfigAttributeEditor,可以理解为一个数组,该数组中存放ConfigAttribute
//每个ConfigAttribute封装一个角色名称
RequestKey key = new RequestKey(entry.getKey(), null);
editor.setAsText(entry.getValue());
requestMap.put(key, (ConfigAttributeDefinition) editor.getValue());
}
return requestMap;
}
/**
* getter
* 返回DefaultFilterInvocationDefinitionSource构造参数之一
*/
protected UrlMatcher getUrlMatcher() {
return new AntUrlPathMatcher();//这个比RegexUrlPathMatcher简单,所以用这个,不求甚解
}
/**
* setter
*
* @param resourceQuery
*/
public void setResourceQuery(String resourceQuery) {
this.resourceQuery = resourceQuery;
}
/**
* 资源实体类POJO
*/
private class Resource {
private String url;
private String role;
public Resource(String url, String role) {
this.url = url;
this.role = role;
}
public String getUrl() {
return url;
}
public String getRole() {
return role;
}
}
/**
* 资源-角色查询内部类
*
*/
private class ResourceMapping extends MappingSqlQuery {
protected ResourceMapping(DataSource dataSource, String resourceQuery) {
super(dataSource, resourceQuery);
compile();
}
/**
* (non-Javadoc)
*
* @see org.springframework.jdbc.object.MappingSqlQuery#mapRow(java.sql.ResultSet, int)
*/
protected Object mapRow(ResultSet rs, int rownum) throws SQLException {
String url = rs.getString(1);
String role = rs.getString(2);
Resource resource = new Resource(url, role);
return resource;
}
}
}
5、这个实例用到的jsp页面都很简单,这里我也放到附近中不再贴出来了,只用把这些页面放在项目的更目录下就好。
到此应该就没问题了