spring Security3.1安全框架完整配置教程

文章转自 :http://www.dev26.com/blog/article/113/1 <!--EndFra-->

前端时间一直在研究spring Security3.1这个框架,感觉用着还算不错,包括本站也是使用是了这个框架正好利用这个机会学习一下.特地给大家分享一下,希望对大家有所帮助.对这个框架的介绍就不用多说了,网上都有相关的资料.我要介绍的是如何利用数据库来存储所有的权限,这样的话就直接可以通过在数据库中增加记录来扩展权限,不用每次都要在XML中配置权限.

为了测试更加方便我用的是haldb数据库,比较简单.

首先到spring官网把最新版的spring Security安全框架的压缩文件下载下来:

http://www.springsource.org/spring-community-download 


然后在Eclipse中新一个Web工程,在web.xml中加入以下权限过滤器来控制来拦截所有访问

1
2
3
4
5
6
7
8
< filter >
     < filter-name >springSecurityFilterChain</ filter-name >
     < filter-class >org.springframework.web.filter.DelegatingFilterProxy</ filter-class >
</ filter >
< filter-mapping >
     < filter-name >springSecurityFilterChain</ filter-name >
     < url-pattern >/*</ url-pattern >
</ filter-mapping >

然后在listener中引入spring 的bean配置文件:

1
2
3
4
5
6
7
   < context-param >
       < param-name >contextConfigLocation</ param-name >
       < param-value >classpath:applicationContext*.xml</ param-value >
   </ context-param >
< listener >
       < listener-class >org.springframework.web.context.ContextLoaderListener</ listener-class >
   </ listener >

整工程截图如下:


以下是hqldb的初始化脚本,也不用建库只要把hqldb.jar放到工程中则会自动执行.这就是工程的test.script的脚本sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
CREATE  SCHEMA  PUBLIC  AUTHORIZATION  DBA
CREATE  MEMORY  TABLE  RESC(ID  BIGINT  GENERATED  BY  DEFAULT  AS  IDENTITY(START  WITH  1)  NOT NULL , NAME  VARCHAR (50),RES_TYPE  VARCHAR (50),RES_STRING  VARCHAR (200),PRIORITY INTEGER ,DESCN  VARCHAR (200), CONSTRAINT  PK_RESC  PRIMARY  KEY (ID))
CREATE  MEMORY  TABLE  ROLE(ID  BIGINT  GENERATED  BY  DEFAULT  AS  IDENTITY(START  WITH  1)  NOT NULL , NAME  VARCHAR (50),DESCN  VARCHAR (200), CONSTRAINT  PK_ROLE  PRIMARY  KEY (ID))
CREATE  MEMORY  TABLE  USER (ID  BIGINT  GENERATED  BY  DEFAULT  AS  IDENTITY(START  WITH  1)  NOT NULL ,USERNAME  VARCHAR (50), PASSWORD  VARCHAR (50),STATUS  INTEGER ,DESCN VARCHAR (200), CONSTRAINT  PK_USER  PRIMARY  KEY (ID))
CREATE  MEMORY  TABLE  RESC_ROLE(RESC_ID  BIGINT  NOT  NULL ,ROLE_ID  BIGINT  NOT NULL , CONSTRAINT  PK_RESC_ROLE  PRIMARY  KEY (RESC_ID,ROLE_ID), CONSTRAINT  FK_RESC_ROLE_RESC FOREIGN  KEY (RESC_ID)  REFERENCES  RESC(ID), CONSTRAINT  FK_RESC_ROLE_ROLE  FOREIGN KEY (ROLE_ID)  REFERENCES  ROLE(ID))
CREATE  MEMORY  TABLE  USER_ROLE(USER_ID  BIGINT  NOT  NULL ,ROLE_ID  BIGINT  NOT NULL , CONSTRAINT  PK_USER_ROLE  PRIMARY  KEY (USER_ID,ROLE_ID), CONSTRAINT  FK_USER_ROLE_USER FOREIGN  KEY (USER_ID)  REFERENCES  USER (ID), CONSTRAINT  FK_USER_ROLE_ROLE  FOREIGN KEY (ROLE_ID)  REFERENCES  ROLE(ID))
ALTER  TABLE  RESC  ALTER  COLUMN  ID RESTART  WITH  3
ALTER  TABLE  ROLE  ALTER  COLUMN  ID RESTART  WITH  3
ALTER  TABLE  USER  ALTER  COLUMN  ID RESTART  WITH  3
CREATE  USER  SA  PASSWORD  ""
GRANT  DBA  TO  SA
SET  WRITE_DELAY 10
SET  SCHEMA  PUBLIC
INSERT  INTO  RESC  VALUES (1, '' , 'URL' , '/admin.jsp' ,1, '' )
INSERT  INTO  RESC  VALUES (2, '' , 'URL' , '/**' ,2, '' )
INSERT  INFO RESC VALUES (3, '' , 'METHOD' , 'com.dev26.springsecuritybook.MessageService.adminMessage' ,3, '' );
INSERT  INTO  ROLE  VALUES (1, 'ROLE_ADMIN' , '\u7ba1\u7406\u5458\u89d2\u8272' )
INSERT  INTO  ROLE  VALUES (2, 'ROLE_USER' , '\u7528\u6237\u89d2\u8272' )
INSERT  INTO  USER  VALUES (1, 'admin' , 'admin' ,1, '\u7ba1\u7406\u5458' )
INSERT  INTO  USER  VALUES (2, 'user' , 'user' ,1, '\u7528\u6237' )
INSERT  INTO  RESC_ROLE  VALUES (1,1)
INSERT  INTO  RESC_ROLE  VALUES (2,1)
INSERT  INTO  RESC_ROLE  VALUES (2,2)
INSERT  INTO  RESC_ROLE  VALUES (3,1)
INSERT  INTO  USER_ROLE  VALUES (1,1)
INSERT  INTO  USER_ROLE  VALUES (1,2)
INSERT  INTO  USER_ROLE  VALUES (2,2)

这些是基本配置文件,然后咱在分析一下spring Security的运行基制,然后在在看另一个applicationContext.xml中的配置就比较容易了.


Spring Security是一个基于角色的权限框架(Role  Based Access Control,RBAC),它通过一系列可配置的组件构建了基于Spring IOC组件装配模式的安全框架.系统中对用于分配角色,而

角色由被授于相应的权限(增加、浏览、删除、更新..),在校验的时候首先要确定用户所属地的角色,然后根据角色来判断当前用户有哪些权限。这只是Spring Security框架的部分功能,其实它比较丰富的配置和扩展比如:(用户组(Group)的配置、ACL权限配置、CAS集成等)。


首先要配置的是一个org.springframework.web.filter.DelegatingFilterProxy的过滤器在webm.xml中这是Spring Security的一个入口,它内部有一个过滤器链。学过Acegi的朋友可能比较了解,这个例子就不深究了。

以下是Secutirity本应的配置,在applicationContext.xml中

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<? xml  version = "1.0"  encoding = "UTF-8" ?>
               
                   
     < http   auto-config = "true" >
                     
     </ http >
     <!--通过SQL语句关联查询当前用户所拥有的权限  -->
< authentication-manager >
         < authentication-provider >
             < jdbc-user-service  data-source-ref = "dataSource"
                 users-by-username-query="select username,password,status as enabled
                                        from user
                                       where username=?"
                 authorities-by-username-query="select u.username,r.name as authority
                                              from user u
                                              join user_role ur
                                                on  u.id = ur .user_id
                                              join role r
                                                on  r.id = ur .role_id
                                             where u.username=?"  />
         </ authentication-provider >
     </ authentication-manager >
               
     < global-method-security  />
               
<!--更新权限缓存,不用每次重启服务  -->
     < beans:bean  id = "resourceDetailsMonitor"
         class = "com.dev26.springsecuritybook.ResourceDetailsMonitor2"
         autowire = "byType" >
         < beans:property  name = "dataSource"  ref = "dataSource"  />
         < beans:property  name = "accessDecisionManager"  ref = "accessDecisionManager"  />
     </ beans:bean >
<!--数据源  -->
     < beans:bean  id = "dataSource"
         class = "org.springframework.jdbc.datasource.DriverManagerDataSource" >
         < beans:property  name = "driverClassName"  value = "org.hsqldb.jdbcDriver"  />
         < beans:property  name = "url"  value = "jdbc:hsqldb:res:/hsqldb/test"  />
         < beans:property  name = "username"  value = "sa"  />
         < beans:property  name = "password"  value = ""  />
     </ beans:bean >
     <!--决策管理器  -->
     < beans:bean  id = "accessDecisionManager"
         class = "org.springframework.security.access.vote.AffirmativeBased" >
         < beans:property  name = "allowIfAllAbstainDecisions"
             value = "false"  />
         < beans:property  name = "decisionVoters" >
             < beans:list >
             < beans:bean  class = "com.dev26.springsecuritybook.MyRoleVoter"  />
                 < beans:bean
                     class = "org.springframework.security.access.vote.AuthenticatedVoter" />
             </ beans:list >
         </ beans:property >
     </ beans:bean >
     < beans:bean  id = "messageService"
         class = "com.dev26.springsecuritybook.MessageServiceImpl"  />
</ beans:beans >

权限缓存刷新类代码ResourceDetailsMonitor2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package  com.dev26.springsecuritybook;
         
import  java.util.ArrayList;
import  java.util.Collection;
import  java.util.List;
         
import  javax.sql.DataSource;
         
import  org.springframework.beans.factory.InitializingBean;
import  org.springframework.security.access.AccessDecisionManager;
import  org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;
import  org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource;
import  org.springframework.security.access.method.MethodSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import  org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
         
import  com.dev26.springsecuritybook.ResourceDetailsBuilder;
         
public  class  ResourceDetailsMonitor2  implements  InitializingBean {
             
       private  String queryUrl =  "select re.res_string,r.name"  +
       "                      from role r"  +
       "                      join resc_role rr"  +
       "                        on r.id=rr.role_id"  +
       "                      join resc re"  +
       "                        on re.id=rr.resc_id"  +
       "                     where re.res_type='URL'"  +
       "                  order by re.priority" private  String queryMethod =
       "select re.res_string,r.name"  "                      from role r"  +
       "                      join resc_role rr"  +
       "                        on r.id=rr.role_id"  +
       "                      join resc re"  +
       "                        on re.id=rr.resc_id"  +
       "                     where re.res_type='METHOD'"  +
       "                  order by re.priority" ;
              
     private  DataSource dataSource;
     private  FilterSecurityInterceptor filterSecurityInterceptor;
     private  org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource delegatingMethodDefinitionSource;
     private  AccessDecisionManager accessDecisionManager;
     private  ResourceDetailsBuilder resourceDetailsBuilder;
     private  MethodSecurityInterceptor methodSecurityInterceptor;
     private  Collection hasMethodAttribute;
     private  Collection hasUrlAttribut;
         
     public  void  setQueryUrl(String queryUrl) {
         this .queryUrl = queryUrl;
     }
         
     public  void  setQueryMethod(String queryMethod) {
         this .queryMethod = queryMethod;
     }
         
     public  void  setDataSource(DataSource dataSource) {
         this .dataSource = dataSource;
     }
         
     public  FilterSecurityInterceptor getFilterSecurityInterceptor() {
         return  filterSecurityInterceptor;
     }
         
     public  void  setFilterSecurityInterceptor(
             FilterSecurityInterceptor filterSecurityInterceptor) {
         this .filterSecurityInterceptor = filterSecurityInterceptor;
     }
         
     public  org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource getDelegatingMethodDefinitionSource() {
         return  delegatingMethodDefinitionSource;
     }
         
     public  void  setDelegatingMethodDefinitionSource(
             org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource delegatingMethodDefinitionSource) {
         this .delegatingMethodDefinitionSource = delegatingMethodDefinitionSource;
     }
         
     public  ResourceDetailsBuilder getResourceDetailsBuilder() {
         return  resourceDetailsBuilder;
     }
         
     public  void  setResourceDetailsBuilder(
             ResourceDetailsBuilder resourceDetailsBuilder) {
         this .resourceDetailsBuilder = resourceDetailsBuilder;
     }
         
     public  String getQueryUrl() {
         return  queryUrl;
     }
         
     public  String getQueryMethod() {
         return  queryMethod;
     }
         
     public  DataSource getDataSource() {
         return  dataSource;
     }
         
     public  void  afterPropertiesSet() {
         resourceDetailsBuilder =  new  ResourceDetailsBuilder(dataSource);
         filterSecurityInterceptor
                 .setAccessDecisionManager(accessDecisionManager);
         this .hasMethodAttribute = delegatingMethodDefinitionSource
                 .getAllConfigAttributes();
         this .hasUrlAttribut = filterSecurityInterceptor
                 .getSecurityMetadataSource().getAllConfigAttributes();
         refresh();
     }
         
     public  void  refresh() {
         if  (filterSecurityInterceptor !=  null ) {
             FilterInvocationSecurityMetadataSource source = resourceDetailsBuilder
                     .createUrlSource(queryUrl);
             source.getAllConfigAttributes().addAll(hasUrlAttribut);
             filterSecurityInterceptor.setSecurityMetadataSource(source);
         }
         if  (delegatingMethodDefinitionSource !=  null ) {
             MethodSecurityMetadataSource source = resourceDetailsBuilder
                     .createMethodSource(queryMethod);
             delegatingMethodDefinitionSource.getMethodSecurityMetadataSources()
                     .clear();
             delegatingMethodDefinitionSource.getMethodSecurityMetadataSources()
                     .add(source);
             List<MethodSecurityMetadataSource> list =  new ArrayList<MethodSecurityMetadataSource>();
             source.getAllConfigAttributes().addAll(hasMethodAttribute);
             list.add(source);
             delegatingMethodDefinitionSource =  new  DelegatingMethodSecurityMetadataSource(
                     list);
             ;
             methodSecurityInterceptor
                     .setSecurityMetadataSource(delegatingMethodDefinitionSource);
             try  {
                 // methodSecurityInterceptor.afterPropertiesSet();
             catch  (Exception e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             }
         }
         
         System.out.println( "-------------------------refresh" );
     }
         
     public  AccessDecisionManager getAccessDecisionManager() {
         return  accessDecisionManager;
     }
         
     public  void  setAccessDecisionManager(
             AccessDecisionManager accessDecisionManager) {
         this .accessDecisionManager = accessDecisionManager;
     }
         
     public  MethodSecurityInterceptor getMethodSecurityInterceptor() {
         return  methodSecurityInterceptor;
     }
         
     public  void  setMethodSecurityInterceptor(
             MethodSecurityInterceptor methodSecurityInterceptor) {
         this .methodSecurityInterceptor = methodSecurityInterceptor;
     }
         
}




扩展决策机制,如果用户有多个角色,只要有一个决色拥有权限则通过校验.MyRoleVoter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package  com.dev26.springsecuritybook;
             
import  java.util.Collection;
             
import  org.springframework.security.access.AccessDecisionVoter;
import  org.springframework.security.access.ConfigAttribute;
import  org.springframework.security.core.Authentication;
import  org.springframework.security.core.GrantedAuthority;
             
public  class  MyRoleVoter  implements  AccessDecisionVoter<Object> {
     private  String rolePrefix =  "ROLE_" ;
             
     public  String getRolePrefix() {
         return  rolePrefix;
     }
             
     public  void  setRolePrefix(String rolePrefix) {
         this .rolePrefix = rolePrefix;
     }
             
     public  boolean  supports(ConfigAttribute attribute) {
         if  ((attribute.getAttribute() !=  null )
                 && attribute.getAttribute().startsWith(getRolePrefix())) {
             return  true ;
         else  {
             return  false ;
         }
     }
             
     /**
      * This implementation supports any type of class, because it does not query
      * the presented secure object.
      *
      * @param clazz
      *            the secure object
      *
      * @return always <code>true</code>
      */
     public  boolean  supports(Class<?> clazz) {
         return  true ;
     }
             
     public  int  vote(Authentication authentication, Object object,
             Collection<ConfigAttribute> attributes) {
         int  result = ACCESS_ABSTAIN;
         Collection<?  extends  GrantedAuthority> authorities = extractAuthorities(authentication);
         for  (ConfigAttribute attribute : attributes) {
             if  ( this .supports(attribute)) {
                 result = ACCESS_DENIED;
                 // Attempt to find a matching granted authority
                 for  (GrantedAuthority authority : authorities) {
                     String[] roles = attribute.getAttribute().split( "," );
                     for  (String string : roles) {
                         if  (string.equals(authority.getAuthority())) {
                             return  ACCESS_GRANTED;
                         }
                     }
             
                 }
             }
         }
         return  result;
     }
             
     Collection<?  extends  GrantedAuthority> extractAuthorities(
             Authentication authentication) {
         return  authentication.getAuthorities();
     }
             
}


以下是业务类,被保护的方法已经在上面的SQL脚本中进行了初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package  com.dev26.springsecuritybook;
             
public  class  MessageServiceImpl  implements  MessageService {
     public  String adminMessage() {
         return  "admin message" ;
     }
             
     public  String adminDate() {
         return  "admin "  + System.currentTimeMillis();
     }
             
     public  String userMessage() {
         return  "user message" ;
     }
             
     public  String userDate() {
         return  "user "  + System.currentTimeMillis();
     }
}


让我来验证一下,首先要编写一个登录页面:login.jsp

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page pageEncoding="UTF-8" %>
           
< html >
   < head >
     < title >Login</ title >
   </ head >
           
   < body  onload = "document.f.j_username.focus();" >
     < h1 >Login</ h1 >
           
     < p >Valid users:
     < p >
     < p >username < b >rod</ b >, password < b >koala</ b >
     < p >username < b >dianne</ b >, password < b >emu</ b >
     < p >username < b >scott</ b >, password < b >wombat</ b >
     < p >username < b >peter</ b >, password < b >opal</ b > (user disabled)
     < p >username < b >bill</ b >, password < b >wombat</ b >
     < p >username < b >bob</ b >, password < b >wombat</ b >
     < p >username < b >jane</ b >, password < b >wombat</ b >
     < p >
           
     < p >Locale is: <%= request.getLocale() %></ p >
     <%-- this form-login-page form is also used as the
          form-error-page to ask for a login again.
          --%>
     < c:if  test = "${not empty param.login_error}" >
       < font  color = "red" >
         Your login attempt was not successful, try again.< br />< br />
         Reason: < c:out  value = "${SPRING_SECURITY_LAST_EXCEPTION.message}" />.
       </ font >
     </ c:if >
           
     < form  name = "f"  action="<c:url  value = 'j_spring_security_check' />" method="POST">
       < table >
         < tr >< td >User:</ td >< td >< input  type = 'text'  name = 'j_username'  value='<c:if test = "${not empty param.login_error}" >< c:out value = "${SPRING_SECURITY_LAST_USERNAME}" /></ c:if >'/></ td ></ tr >
         < tr >< td >Password:</ td >< td >< input  type = 'password'  name = 'j_password' ></ td ></ tr >
         < tr >< td >< input  type = "checkbox"  name = "_spring_security_remember_me" ></ td >< td >Don't ask for my password for two weeks</ td ></ tr >
           
         < tr >< td  colspan = '2' >< input  name = "submit"  type = "submit" ></ td ></ tr >
         < tr >< td  colspan = '2' >< input  name = "reset"  type = "reset" ></ td ></ tr >
       </ table >
           
     </ form >
           
   </ body >
</ html >

如果通过校验则进入idnex.jsp

 

1
2
3
4
5
6
7
8
9
10
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
< div >username :< security:authentication  property = "principal.username" /></ div >
< hr >
< a  href = "admin.jsp" >admin.jsp</ a >
|
< a  href = "admin-method.jsp" >admin-method.jsp</ a >
|
< a  href = "user-method.jsp" >user-method.jsp</ a >
|
< a  href = "j_spring_security_logout" >logout</ a >

如果你是用普通户登录的,如果进行admin-method.jsp则会被拦截,user-method.jsp则可以访问,如果有物是admin的角色由都可以进行访问.






如果点击admin.jsp



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值