Spring Security 之从数据库加载访问资源列表

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Security 之从数据库加载访问资源列表相关的知识,希望对你有一定的参考价值。

  关于数据拦截列表,SS官方提供的例子是基于配置的简单实现,例如以下代码:

  

1 <http>
2   <intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
3   <intercept-url pattern="/**" access="ROLE_USER" />
4   <form-login login-page=‘/login.jsp‘/>
5 </http>

  这样的硬性代码加入配置自然不能满足复杂的业务需求,最好的实现就是将所有的数据使用数据库配置,然后可以自由的添加删除而不影响程序。

  数据资源列表一般应用在请求资源的时候,根据访问的资源,来获取当前资源所需要的权限列表,然后根据用户的权限,判断是否允许通过。所以数据资源列表在访问时候相当于数据源。我们所有的请求应当都能在数据资源列表中找到。

  自定义数据资源列表应该实现FilterInvocationSecurityMetadataSource接口,ss默认实现是DefaultFilterInvocationSecurityMetadataSource。它有三个实现方法,分别是getAllConfigAttributes(),返回全部的配置集合。getAttributes(Object object),根据当前访问的对象返回相应的配置。supports(Class<?> clazz),根据当前对象决定是否支持该类操作。此处重点是根据当前请求资源获取配置,即getAttributes(Object object)方法。该方法是根据object返回一个该object对应的配置,通常情况下,object是一个FilterInvocation,其中可以获取到url资源,那么可以理解为该url有哪些配置(权限)。所以得根据url存储权限,用map最好不过了。

  

 1 public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
 2 
 3     @Autowired
 4     private ResourceService resourceService;
 5 
 6     private Map<String, Collection<ConfigAttribute>> resource;
 7 
 8     private void loadResource() {
 9         resource = new LinkedHashMap<String, Collection<ConfigAttribute>>();
10         Map<String, List<Privilege>> resource_privilege = resourceService.getResourcePrivilege();
11         Iterator<String> iterator = resource_privilege.keySet().iterator();
12         while (iterator.hasNext()) {
13             String url = (String) iterator.next();
14             List<Privilege> privileges = resource_privilege.get(url);
15             Collection<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
16             for (Privilege privilege : privileges) {
17                 ConfigAttribute attribute = new SecurityConfig(privilege.getId().toString());
18                 attributes.add(attribute);
19             }
20             resource.put(url, attributes);
21         }
22     }
23 
24     @Override
25     public Collection<ConfigAttribute> getAllConfigAttributes() {
26         return null;
27     }
28 
29     @Override
30     public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
31         // 每次从数据库加载当前所请求的资源
32         System.out.println("请求 " + object + " 资源");
33         loadResource();
34         HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
35         Iterator<String> iterator = resource.keySet().iterator();
36         while (iterator.hasNext()) {
37             String url = (String) iterator.next();
38             AntPathRequestMatcher antPathRequestMatcher = new AntPathRequestMatcher(url);
39             if (antPathRequestMatcher.matches(request)) {
40                 System.out.println("资源配置的权限id列表:" + resource.get(url));
41                 return resource.get(url);
42             }
43         }
44         return null;
45     }
46 
47     @Override
48     public boolean supports(Class<?> clazz) {
49         return FilterInvocation.class.isAssignableFrom(clazz);
50     }
51 
52 }

   在38行代码中,使用了一个AntPathRequestMatcher作为url的匹配规则,AntPathRequestMatcher的匹配规则如下:

  1. * 任意匹配多个字符,但不能跨越目录

  2. ** 任意匹配多个字符,可以跨越目录

  3.  ?   任意匹配一个字符

  通常情况下AntPathRequestMatcher可以满足我们的要求,如果不能满足复杂的业务,可以使用RegexRequestMatcher,它支持正则表达式来进行匹配。

  在getAttributes方法中,调用了loadResource方法,在loadResource方法中,请求了存放数据资源的数据表。该表和权限关联起来,entity如下:

技术分享
 1 @Entity
 2 public class MetaResource implements Serializable {
 3 
 4     private static final long serialVersionUID = 1L;
 5 
 6     @Id
 7     @GeneratedValue
 8     private Long id;
 9     
10     @Column
11     private String url;
12     
13     @Column
14     private  String resourceName;
15     
16     @Column
17     private  Long sequence=0L;
18     
19     @ManyToMany(fetch=FetchType.EAGER)
20     @LazyCollection(LazyCollectionOption.FALSE)
21     @JoinTable(name="resource_privilege",
22     joinColumns={@JoinColumn(name="s_id",referencedColumnName="id")},
23     inverseJoinColumns={@JoinColumn(name="p_id",referencedColumnName="id")})
24     private List<Privilege> privileges;
25 
26     public Long getId() {
27         return id;
28     }
29 
30     public void setId(Long id) {
31         this.id = id;
32     }
33 
34     public String getUrl() {
35         return url;
36     }
37 
38     public void setUrl(String url) {
39         this.url = url;
40     }
41 
42     public List<Privilege> getPrivileges() {
43         return privileges;
44     }
45 
46     public void setPrivileges(List<Privilege> privileges) {
47         this.privileges = privileges;
48     }
49 
50     public String getResourceName() {
51         return resourceName;
52     }
53 
54     public void setResourceName(String resourceName) {
55         this.resourceName = resourceName;
56     }
57     
58     public Long getSequence() {
59         return sequence;
60     }
61 
62     public void setSequence(Long sequence) {
63         this.sequence = sequence;
64     }
65 }
View Code

  在entity中,加入了一个sequence属性,该属性是为了记录url的循序的,等同于在http里配置的拦截url的循序。ss的url循序是特殊优先,也就是说那些单独需要设置权限或者有特殊权限的需要放在前面,因为不这样做,有可能在前一个url就被拦截,后面的根本访问不到,所以我们一般在http中配置的时候,会把登录等无权限的页面放在最前,以防止登录页面都不能访问。

  获取权限资源的service方法如下,此处注意使用的LinkedHashMap以保障我们存入是数据不会乱序

 1     @SuppressWarnings("unchecked")
 2     public Map<String, List<Privilege>> getResourcePrivilege(){
 3         String queryString ="SELECT $ FROM MetaResource $ order by $.sequence asc";
 4         Query query = em.createQuery(queryString);
 5 //        query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
 6         List<MetaResource> resources = query.getResultList();
 7         Map<String, List<Privilege>> results = new LinkedHashMap<String, List<Privilege>>();
 8         for (MetaResource metaResource : resources) {
 9             results.put(metaResource.getUrl(), (List<Privilege>) metaResource.getPrivileges());
10         }
11         return results;
12     }

 

以上是关于Spring Security 之从数据库加载访问资源列表的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security 静态资源访问

[死磕 Spring 17/43] --- IOC 之从单例缓存中获取单例 bean

使用Spring Security从LDAP加载组

死磕 Spring----- IOC 之从单例缓存中获取单例 bean

Spring Boot Security 自定义登录页面未加载

Spring Security----RBAC权限控制模型,和权限相关知识点整理