- 本文章摘编、转载需要注明来源 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>