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 }

  在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     }
时间: 2024-10-09 00:50:43

Spring Security 之从数据库加载访问资源列表的相关文章

Spring:启动项目时加载数据库数据(总结)

在项目中需要启动程序时,要将数据库的用户信息表加载到内存中,找到一下几种方式. 1.实现ApplicationListener接口,重写onApplicationEvent方法,可以在项目启动的时候执行该方法. @Component("userInit") public class UserInit implements ApplicationListener { public static Map<String,User> map=new HashMap<Strin

spring mvc 加载静态资源

由于我们在web.xml进行了如下配置: <servlet> <servlet-name>spring_mvc_demo</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> &l

你所不知道的SQL Server数据库启动过程(用户数据库加载过程的疑难杂症)

前言 本篇主要是上一篇文章的补充篇,上一篇我们介绍了SQL Server服务启动过程所遇到的一些问题和解决方法,可点击查看,我们此篇主要介绍的是SQL Server启动过程中关于用户数据库加载的流程,并且根据加载过程中所遇到的一系列问题提供解决方案. 其实SQL Server作为微软的一款优秀RDBMS,它启动的过程中,本身所带的那些系统库发生问题的情况相对还是很少的,我们在平常使用中,出问题的大部分集中于我们自己建立的用户数据库. 而且,相对于侧重面而言,其实我们更关注的是我们自己建立的用户数

Spring mvc应用 加载静态资源的几种方式

总结几种Spring mvc应用加载静态资源的方式 1.使用服务器的默认Servlet处理 对于不同的服务器,处理静态资源的servlet-name不一样,需要去看服务器具体的配置文件 比如resin-3.1.12,通过查看app-default.xml可以看到默认处理jsp的servlet-name为>resin-jsp 所以可以通过在web.xml中添加静态资源的访问 <servlet-mapping>        <servlet-name>resin-jsp<

单点登录CAS与Spring Security集成(数据库验证,向客户端发送更多信息)

准备工作 CAS server从网上直接下载下来,里面有一个cas-server-webapp的工程,使用Maven命令构建,导入到Eclipse中,便可以直接使用,cas server我使用的是3.5.2版本.客户端,我是使用以前的工程,只要是Web工程就行,cas-client使用的3.2.1,Spring Security使用的是3.1.4,记得Spring Security的3.1.2版本和CAS集成时,当需要CAS Server传比较多的信息给客户端时,客户端的Spring Secur

看看Spring的源码——Bean加载过程

最近几天跟同事聊起Spring的一些问题,对一些地方有些疑问,趁这两天有点空,看看Spring的源码,了解下具体的实现细节.本文基于Spring 4.0.5版本. 首先Web项目使用Spring是通过在web.xml里面配置org.springframework.web.context.ContextLoaderListener初始化IOC容器的. <listener> <listener-class>org.springframework.web.context.ContextL

配置Spring的用于解决懒加载问题的过滤器

<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://j

spring启动component-scan类扫描加载过程---源码分析

有朋友最近问到了 spring 加载类的过程,尤其是基于 annotation 注解的加载过程,有些时候如果由于某些系统部署的问题,加载不到,很是不解!就针对这个问题,我这篇博客说说spring启动过程,用源码来说明,这部分内容也会在书中出现,只是表达方式会稍微有些区别,我将使用spring 3.0的版本来说明(虽然版本有所区别,但是变化并不是特别大),另外,这里会从WEB中使用spring开始,中途会穿插自己通过newClassPathXmlApplicationContext 的区别和联系.

spring启动component-scan类扫描加载过程(转)

文章转自 http://www.it165.net/pro/html/201406/15205.html 有朋友最近问到了 spring 加载类的过程,尤其是基于 annotation 注解的加载过程,有些时候如果由于某些系统部署的问题,加载不到,很是不解!就针对这个问题,我这篇博客说说spring启动过程,用源码来说明,这部分内容也会在书中出现,只是表达方式会稍微有些区别,我将使用spring 3.0的版本来说明(虽然版本有所区别,但是变化并不是特别大),另外,这里会从WEB中使用spring