本文章摘编、转载需要注明来源 http://write.blog.csdn.net/postedit/8575062
spring security3中的权限管理虽然有文件可配置,但是很多时候我们是需要数据库的支持,下面我演示下如何配置自定义权限管理,这个时候需要重新实现下面的类,
该文章适合对spring security3 有一定理解的人员
AccessDecisionManager是验证资源跟角色之间的关系,由于我个人不太喜欢用标签化,因为感觉灵活性不够好,所以我统一是用bean方式,至于用bean来描述是需要对security的
过滤链流程和各个属性依赖关系比较熟悉的了解才可以配置成功,这样灵活性大大加强
/**
*
* @author shadow
* @email 124010356@qq.com
* @create 2012.04.28
*/
public class AccessDecisionManagerImpl implements AccessDecisionManager {
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> attributes)
throws AccessDeniedException, InsufficientAuthenticationException {
if (null == attributes)
return;
for (ConfigAttribute attribute : attributes) {
String needRole = ((SecurityConfig) attribute).getAttribute();
// authority为用户所被赋予的权限, needRole 为访问相应的资源应该具有的权限。
for (GrantedAuthority grantedAuthority : authentication
.getAuthorities()) {
if (needRole.equals(grantedAuthority.getAuthority()))
return;
}
}
throw new AccessDeniedException("权限不足!");
}
public boolean supports(ConfigAttribute attribute) {
return true;
}
public boolean supports(Class<?> clazz) {
return true;
}
}
SecurityMetadataSource是角色跟资源加载器,项目启动的时候会先执行资源跟角色关联加载提供给security以便认证
/**
* 初始化时加载角色资源关联数据
*
* @author shadow
* @email 124010356@qq.com
* @create 2012.04.28
*/
public class SecurityMetadataSourceExtendImpl implements
SecurityMetadataSourceExtend {
private boolean expire = false; // 过期标识
private RoleService roleService; // 角色服务类
private ResourceService resourceService; // 资源服务类
private RequestMatcher requestMatcher; // 匹配规则
private String matcher; // 规则标识
private Map<String, Collection<ConfigAttribute>> kv = new HashMap<String, Collection<ConfigAttribute>>(); // 资源集合
public RoleService getRoleService() {
return roleService;
}
@javax.annotation.Resource
public void setRoleService(RoleService roleService) {
this.roleService = roleService;
}
public ResourceService getResourceService() {
return resourceService;
}
@javax.annotation.Resource
public void setResourceService(ResourceService resourceService) {
this.resourceService = resourceService;
}
public boolean supports(Class<?> clazz) {
return true;
}
// 初始化方法时候从数据库中读取资源
// @PostConstruct
public void init() {
load();
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
Set<ConfigAttribute> attributes = new HashSet<ConfigAttribute>();
for (Map.Entry<String, Collection<ConfigAttribute>> entry : kv
.entrySet()) {
attributes.addAll(entry.getValue());
}
return attributes;
}
public Collection<ConfigAttribute> getAttributes(Object object)
throws IllegalArgumentException {
HttpServletRequest request = ((FilterInvocation) object).getRequest();
// System.out.println("requestUrl is " + request.getRequestURI());
// 检测是否刷新了资源
if (isExpire()) {
// 清空原本资源
kv.clear();
expire = false;
}
// 如果资源Map为空的时候则重新加载一次
if (null == kv || kv.isEmpty())
load();
// 检测请求与当前资源匹配的正确性
Iterator<String> iterator = kv.keySet().iterator();
while (iterator.hasNext()) {
String uri = iterator.next();
if (matcher.toLowerCase().equals("ant")) {
requestMatcher = new AntPathRequestMatcher(uri);
}
if (matcher.toLowerCase().equals("regex")) {
requestMatcher = new RegexRequestMatcher(uri, request
.getMethod(), true);
}
if (requestMatcher.matches(request))
return kv.get(uri);
}
return null;
}
/**
* 加载所有资源与权限的关系
*/
public void load() {
List<Resource> resources = this.resourceService.loadForAll();
for (Resource resource : resources) {
List<Role> roles = this.roleService.findByResourceId(resource
.getId());
kv.put(resource.getContent(), list2Collection(roles));
}
}
/**
* 将List<Role>集合转换为框架需要的Collection<ConfigAttribute>集合
*
* @param roles
* @return Collection<ConfigAttribute>
*/
private Collection<ConfigAttribute> list2Collection(List<Role> roles) {
List<ConfigAttribute> list = new ArrayList<ConfigAttribute>();
for (Role role : roles)
list.add(new SecurityConfig(role.getName()));
return list;
}
public void setMatcher(String matcher) {
this.matcher = matcher;
}
public boolean isExpire() {
return expire;
}
public void expireNow() {
this.expire = true;
}
}
FilterSecurityInterceptor是资源访问第一个需要经过的过滤器,这个类我们还是不需要重写了,直接使用spring security提供的比较
具体路径org.springframework.security.web.access.intercept.FilterSecurityInterceptor
UserDetailsService这个类security的form表单登录处理
/**
* SPRING SECURITY3用户登录处理
*
* @author shadow
* @email 124010356@qq.com
* @create 2012.04.28
*/
public class UserDetailsServiceImpl implements UserDetailsService {
private UserService userService;
private RoleService roleService;
public UserService getUserService() {
return userService;
}
@Resource
public void setUserService(UserService userService) {
this.userService = userService;
}
public RoleService getRoleService() {
return roleService;
}
@Resource
public void setRoleService(RoleService roleService) {
this.roleService = roleService;
}
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
// 使用User服务类查询数据用户是否存在,如不存在或密码错误则抛出对应的异常
List<User> users = this.userService.findByUserName(username);
if (null == users || users.isEmpty())
throw new UsernameNotFoundException("用户/密码错误,请重新输入!");
User user = users.get(0);
List<Role> roles = this.roleService.findByUserId(user.getId());
if (null == roles || roles.isEmpty())
throw new UsernameNotFoundException("权限不足!");
// 把权限赋值给当前对象
Collection<GrantedAuthority> gaRoles = new ArrayList<GrantedAuthority>();
for (Role role : roles) {
gaRoles.add(new SimpleGrantedAuthority(role.getName()));
}
user.setAuthorities(gaRoles);
return user;
}
}
三个类都准备好了现在去配置xml文件,先声明三个类的bean
<!-- 自定义UserDetailsService认证 -->
<bean id="userDetailsService"
class="com.shadow.security.service.UserDetailsServiceImpl" />
<!-- 自定义资源权限关系认证 -->
<bean id="accessDecisionManager"
class="com.shadow.security.service.AccessDecisionManagerImpl" />
<!-- 自定义资源权限关系集合 -->
<bean id="securityMetadataSource"
class="com.shadow.security.service.SecurityMetadataSourceExtendImpl">
<property name="matcher" value="ant" />
</bean>
<!-- 自定义认证管理,资源,权限 -->
<bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager"
ref="authenticationManager" />
<property name="accessDecisionManager"
ref="accessDecisionManager" />
<property name="securityMetadataSource"
ref="securityMetadataSource" />
</bean>
至于authenticationManager的注入如下(rememberMeAuthenticationProvider可不注入,这个东西是记住密码功能需要用到的玩意)
<!-- 认证管理器 -->
<bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref bean="daoAuthenticationProvider" />
<ref bean="rememberMeAuthenticationProvider" />
</list>
</property>
</bean>
<!-- 登录认证处理 -->
<bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="hideUserNotFoundExceptions" value="false"/>
<property name="userDetailsService" ref="userDetailsService" />
<property name="passwordEncoder" ref="passwordEncoder" />
<property name="saltSource" ref="saltSource" />
</bean>
<!-- 加密方式 -->
<bean id="passwordEncoder"
class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />
<!-- 配置加密盐值 -->
<bean id="saltSource"
class="org.springframework.security.authentication.dao.ReflectionSaltSource">
<property name="userPropertyToUse" value="username" />
</bean>
然后配置我们的过滤链
<!-- 自定义SPRING SECURITY过滤链 -->
<bean id="securityFilterChainProxy"
class="org.springframework.security.web.FilterChainProxy">
<constructor-arg>
<list>
<security:filter-chain pattern="/services/**"
filters="none" />
<security:filter-chain pattern="/test*" filters="none" />
<security:filter-chain pattern="/**"
filters="concurrentSessionFilter,securityContextPersistenceFilter,logoutFilter,usernamePasswordAuthenticationFilter,rememberMeAuthenticationFilter,sessionManagementFilter,anonymousAuthFilter,exceptionTranslationFilter,filterSecurityInterceptor" />
</list>
</constructor-arg>
</bean>