本文章摘编、转载需要注明来源 http://write.blog.csdn.net/postedit/8575062
spring security3中的权限管理虽然有文件可配置,但是很多时候我们是需要数据库的支持,下面我演示下如何配置自定义权限管理,这个时候需要重新实现下面的类,
该文章适合对spring security3 有一定理解的人员
AccessDecisionManager是验证资源跟角色之间的关系,由于我个人不太喜欢用标签化,因为感觉灵活性不够好,所以我统一是用bean方式,至于用bean来描述是需要对security的
过滤链流程和各个属性依赖关系比较熟悉的了解才可以配置成功,这样灵活性大大加强
/** * * @author shadow * @email [email protected] * @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 [email protected] * @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 [email protected] * @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>
<!-- 认证管理器 --> <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>