Spring Security Config : HttpSecurity安全配置器 ExpressionUrlAuthorizationConfigurer

概述
介绍
作为一个配置HttpSecurity的SecurityConfigurer,ExpressionUrlAuthorizationConfigurer的配置任务如下 :

配置如下安全过滤器Filter
FilterSecurityInterceptor
ExpressionUrlAuthorizationConfigurer可以为多组 RequestMatcher 分别配置不同的权限属性。这里每组RequestMatcher表示一组调用者想设定成相同权限控制的Http method/URL pattern,这里所设置的权限属性其实是基于SpEL的权限表达式。ExpressionUrlAuthorizationConfigurer可以接收一个调用者指定的安全表达式处理器来理解这些权限表达式。如果调用者不指定,则使用缺省的安全表达式处理器DefaultWebSecurityExpressionHandler来理解这些权限表达式。

ExpressionUrlAuthorizationConfigurer继承自AbstractInterceptUrlConfigurer。作为一个安全配置器,它们对目标安全构建器HttpSecurity的主要配置逻辑实现在AbstractInterceptUrlConfigurer#configure。ExpressionUrlAuthorizationConfigurer主要是根据基类AbstractInterceptUrlConfigurer的定义,提供相应的抽象方法的实现。

继承关系


使用
例子 1
    // HttpSecurity 代码片段
    public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests()
            throws Exception {
        ApplicationContext context = getContext();
        return getOrApply(new ExpressionUrlAuthorizationConfigurer<>(context))
                .getRegistry();
    }
1
2
3
4
5
6
7
例子 2 : 请求任意URL的访问者必须拥有指定权限
 // 该例子使用 ExpressionUrlAuthorizationConfigurer 配置请求任何一个URL的访问者必须拥有 USER 权限
 @Configuration
 @EnableWebSecurity
 public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
                http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin();
        }

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                auth.inMemoryAuthentication().withUser("user").password("password").roles("USER")
                                .and().withUser("admin").password("password").roles("ADMIN", "USER");
        }
 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
例子 3 : 请求不同URL的访问者必须拥有相应权限
// 该例子使用 ExpressionUrlAuthorizationConfigurer 配置 : 
// 1. 请求 /admin/** 这种URL的访问者必须拥有 ADMIN 权限,
// 2. 请求 /admin/** 这种URL之外的其他任意URL 的访问者必须拥有 USER 权限,
 @Configuration
 @EnableWebSecurity
 public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
                http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN")
                                .antMatchers("/**").hasRole("USER").and().formLogin();
        }

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                auth.inMemoryAuthentication().withUser("user").password("password").roles("USER")
                                .and().withUser("admin").password("password").roles("ADMIN", "USER");
        }
 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
源代码
源代码版本 Spring Security Config 5.1.4.RELEASE

package org.springframework.security.config.annotation.web.configurers;

// 省略 imports


public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
        extends
        AbstractInterceptUrlConfigurer<ExpressionUrlAuthorizationConfigurer<H>, H> {
    static final String permitAll = "permitAll";
    private static final String denyAll = "denyAll";
    private static final String anonymous = "anonymous";
    private static final String authenticated = "authenticated";
    private static final String fullyAuthenticated = "fullyAuthenticated";
    private static final String rememberMe = "rememberMe";

   // 本安全配置器所使用的 URL pattern 和 所需权限 的注册表,也是映射表,
   // 使用实现类为 ExpressionInterceptUrlRegistry, 一个本类的内部实现类,
   // 继承自基类的 AbstractInterceptUrlRegistry ,通过该注册表类可以为当前
   // 安全配置器指定一个安全表达式处理器 SecurityExpressionHandler<FilterInvocation>,
   // 也就是指定下面的属性变量 expressionHandler
    private final ExpressionInterceptUrlRegistry REGISTRY;

    private SecurityExpressionHandler<FilterInvocation> expressionHandler;

    /**
     * Creates a new instance
     * @see HttpSecurity#authorizeRequests()
     */
    public ExpressionUrlAuthorizationConfigurer(ApplicationContext context) {
       //  在构造函数中创建 REGISTRY 为一个 ExpressionInterceptUrlRegistry 实例
        this.REGISTRY = new ExpressionInterceptUrlRegistry(context);
    }

    
    public ExpressionInterceptUrlRegistry getRegistry() {
        return REGISTRY;
    }

    // 内部嵌套类,非静态类,本安全配置器所使用的 URL pattern 和 所需权限 的注册表,也是映射表,
    // 的实现类,继承自基类的 AbstractInterceptUrlRegistry ,通过该注册表类可以为当前
   // 安全配置器指定一个安全表达式处理器 SecurityExpressionHandler<FilterInvocation>
    public class ExpressionInterceptUrlRegistry
            extends ExpressionUrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry
            <ExpressionInterceptUrlRegistry, AuthorizedUrl> {

        /**
         * @param context
         */
        private ExpressionInterceptUrlRegistry(ApplicationContext context) {
            setApplicationContext(context);
        }

       // 针对指定的 HTTP method, 和 mvcPatterns 构造一组 RequestMatcher, 包装在一个 
       // MvcMatchersAuthorizedUrl 对象中,最终这组 RequestMatcher 上会应用相同的
       // 权限设置
        @Override
        public MvcMatchersAuthorizedUrl mvcMatchers(HttpMethod method, String... mvcPatterns) {
            return new MvcMatchersAuthorizedUrl(createMvcMatchers(method, mvcPatterns));
        }

       // 针对指定的 patterns 构造一组 RequestMatcher, 包装在一个 
       // MvcMatchersAuthorizedUrl 对象中,最终这组 RequestMatcher 上会应用相同的
       // 权限设置
       // 本方法其实是使用了上面的方法
       // MvcMatchersAuthorizedUrl mvcMatchers(HttpMethod method, String... mvcPatterns)
        @Override
        public MvcMatchersAuthorizedUrl mvcMatchers(String... patterns) {
            return mvcMatchers(null, patterns);
        }

        @Override
        protected final AuthorizedUrl chainRequestMatchersInternal(
                List<RequestMatcher> requestMatchers) {
            return new AuthorizedUrl(requestMatchers);
        }

        /**
         * Allows customization of the SecurityExpressionHandler to be used. The
         * default is DefaultWebSecurityExpressionHandler
         * 为当前 ExpressionInterceptUrlRegistry 设置安全表达式处理器,如果不设置
         * 缺省值为一个 DefaultWebSecurityExpressionHandler 
         * @param expressionHandler the SecurityExpressionHandler to be used
         * @return the ExpressionUrlAuthorizationConfigurer for further
         * customization.
         */
        public ExpressionInterceptUrlRegistry expressionHandler(
                SecurityExpressionHandler<FilterInvocation> expressionHandler) {
            ExpressionUrlAuthorizationConfigurer.this.expressionHandler = expressionHandler;
            return this;
        }

        /**
         * Adds an ObjectPostProcessor for this class.
         *
         * @param objectPostProcessor
         * @return the ExpressionUrlAuthorizationConfigurer for further
         * customizations
         */
        public ExpressionInterceptUrlRegistry withObjectPostProcessor(
                ObjectPostProcessor<?> objectPostProcessor) {
            addObjectPostProcessor(objectPostProcessor);
            return this;
        }

        public H and() {
            return ExpressionUrlAuthorizationConfigurer.this.and();
        }

    }

    /**
     * Allows registering multiple RequestMatcher instances to a collection of
     * ConfigAttribute instances
     *
     * 注册一组 RequestMatcher requestMatchers,这组 requestMatchers 中每一个
     * RequestMatcher 都会映射到所需权限 configAttributes
     * @param requestMatchers the RequestMatcher instances to register to the
     * ConfigAttribute instances
     * @param configAttributes the ConfigAttribute to be mapped by the
     * RequestMatcher instances
     */
    private void interceptUrl(Iterable<? extends RequestMatcher> requestMatchers,
            Collection<ConfigAttribute> configAttributes) {
        for (RequestMatcher requestMatcher : requestMatchers) {
            // 将 requestMatchers 中每一个RequestMatcher 和 configAttributes 构造一个
            // UrlMapping 对象,也就是 <URL pattern,所需权限>对儿,添加到注册表 REGISTRY
            REGISTRY.addMapping(new AbstractConfigAttributeRequestMatcherRegistry.UrlMapping(
                    requestMatcher, configAttributes));
        }
    }

    // 基类定义的抽象方法,要求实现类必须提供实现。
    // 这里提供实现,创建缺省 AccessDecisionManager 时要用。
    // 这里的实现其实提供了一个 WebExpressionVoter, 使用设置给本类的安全表达式处理器
    // 或者缺省的安全表达式处理器,具体由方法 getExpressionHandler 决定
    @Override
    @SuppressWarnings("rawtypes")
    final List<AccessDecisionVoter<? extends Object>> getDecisionVoters(H http) {
        List<AccessDecisionVoter<? extends Object>> decisionVoters = 
            new ArrayList<AccessDecisionVoter<? extends Object>>();
        WebExpressionVoter expressionVoter = new WebExpressionVoter();
        expressionVoter.setExpressionHandler(getExpressionHandler(http));
        decisionVoters.add(expressionVoter);
        return decisionVoters;
    }
    // 基类定义的抽象方法,要求实现类必须提供实现。
    // 这里提供实现,创建目标安全拦截过滤器 FilterSecurityInterceptor 时要用。
    // 这里的实现基于调用者提供的配置所形成的 <URL pattern,所需权限>映射信息,也就是 
    // REGISTRY 生成一个 ExpressionBasedFilterInvocationSecurityMetadataSource 对象,
    // 该对象会最终被设置到目标安全拦截过滤器 FilterSecurityInterceptor 上。
    // FilterSecurityInterceptor 会在处理一个请求时,通过该 
    // ExpressionBasedFilterInvocationSecurityMetadataSource 对象获取被请求URL所需的权限,
    // 另外获取请求者所拥有的的权限,从而判断请求这是否可以访问相应资源。
    @Override
    final ExpressionBasedFilterInvocationSecurityMetadataSource createMetadataSource(
            H http) {
        LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = REGISTRY
                .createRequestMap();
        if (requestMap.isEmpty()) {
            throw new IllegalStateException(
    "At least one mapping is required (i.e. authorizeRequests().anyRequest().authenticated())");
        }
        return new ExpressionBasedFilterInvocationSecurityMetadataSource(requestMap,
                getExpressionHandler(http));
    }

    private SecurityExpressionHandler<FilterInvocation> getExpressionHandler(H http) {
       // 如果调用者设置了属性  expressionHandler 使用之,
       // 否则使用缺省值 DefaultWebSecurityExpressionHandler
        if (expressionHandler == null) {
          // 创建缺省 DefaultWebSecurityExpressionHandler 对象,设置到 expressionHandler 属性
            DefaultWebSecurityExpressionHandler defaultHandler = 
                new DefaultWebSecurityExpressionHandler();
            AuthenticationTrustResolver trustResolver = http
                    .getSharedObject(AuthenticationTrustResolver.class);
            if (trustResolver != null) {
                defaultHandler.setTrustResolver(trustResolver);
            }
            ApplicationContext context = http.getSharedObject(ApplicationContext.class);
            if (context != null) {
              // 检查是否有 RoleHierarchy bean 可以应用,若检测到则应用
                String[] roleHiearchyBeanNames = context.getBeanNamesForType(RoleHierarchy.class);
                if (roleHiearchyBeanNames.length == 1) {
                    defaultHandler.setRoleHierarchy(context.getBean(roleHiearchyBeanNames[0], 
                        RoleHierarchy.class));
                }
              
              // 检测是否有 GrantedAuthorityDefaults bean 可以应用,若检测到则应用
                String[] grantedAuthorityDefaultsBeanNames = 
                    context.getBeanNamesForType(GrantedAuthorityDefaults.class);
                if (grantedAuthorityDefaultsBeanNames.length == 1) {
                    GrantedAuthorityDefaults grantedAuthorityDefaults = 
                        context.getBean(grantedAuthorityDefaultsBeanNames[0], 
                            GrantedAuthorityDefaults.class);
                    defaultHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
                }
              
              // 检测是否有 PermissionEvaluator bean 可以应用,若检测到则应用
                String[] permissionEvaluatorBeanNames = 
                    context.getBeanNamesForType(PermissionEvaluator.class);
                if (permissionEvaluatorBeanNames.length == 1) {
                    PermissionEvaluator permissionEvaluator = 
                        context.getBean(permissionEvaluatorBeanNames[0], 
                        PermissionEvaluator.class);
                    defaultHandler.setPermissionEvaluator(permissionEvaluator);
                }
            }
          
          // 新建对象的后置处理
            expressionHandler = postProcess(defaultHandler);
        }

        return expressionHandler;
    }

    // 表达式字符串构建工厂方法
    private static String hasAnyRole(String... authorities) {
        String anyAuthorities = StringUtils.arrayToDelimitedString(authorities,
                "','ROLE_");
        return "hasAnyRole('ROLE_" + anyAuthorities + "')";
    }

   // 表达式字符串构建工厂方法
    private static String hasRole(String role) {
        Assert.notNull(role, "role cannot be null");
        if (role.startsWith("ROLE_")) {
            throw new IllegalArgumentException(
                    "role should not start with 'ROLE_' since it is automatically inserted. Got '"
                            + role + "'");
        }
        return "hasRole('ROLE_" + role + "')";
    }

    // 表达式字符串构建工厂方法
    private static String hasAuthority(String authority) {
        return "hasAuthority('" + authority + "')";
    }

    // 表达式字符串构建工厂方法
    private static String hasAnyAuthority(String... authorities) {
        String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','");
        return "hasAnyAuthority('" + anyAuthorities + "')";
    }

    // 表达式字符串构建工厂方法
    private static String hasIpAddress(String ipAddressExpression) {
        return "hasIpAddress('" + ipAddressExpression + "')";
    }

    /**
     * An AuthorizedUrl that allows optionally configuring the
     * MvcRequestMatcher#setMethod(HttpMethod)
     *
     * @author Rob Winch
     */
    public class MvcMatchersAuthorizedUrl extends AuthorizedUrl {
        /**
         * Creates a new instance
         *
         * @param requestMatchers the RequestMatcher instances to map
         */
        private MvcMatchersAuthorizedUrl(List<MvcRequestMatcher> requestMatchers) {
            super(requestMatchers);
        }

        public AuthorizedUrl servletPath(String servletPath) {
            for (MvcRequestMatcher matcher : (List<MvcRequestMatcher>) getMatchers()) {
                matcher.setServletPath(servletPath);
            }
            return this;
        }
    }

    // 非静态内部类,主要是提供一组方法,便于往当前 ExpressionUrlAuthorizationConfigurer 中
    // 添加一组需要共同权限设置的 RequestMatcher,并对这组 RequestMatcher 设置相同的权限控制。
    public class AuthorizedUrl {
        private List<? extends RequestMatcher> requestMatchers;
        private boolean not;

        /**
         * Creates a new instance
         *
         * @param requestMatchers the RequestMatcher instances to map
         */
        private AuthorizedUrl(List<? extends RequestMatcher> requestMatchers) {
            this.requestMatchers = requestMatchers;
        }

        protected List<? extends RequestMatcher> getMatchers() {
            return this.requestMatchers;
        }

        /**
         * Negates the following expression.
         *
         * @return the ExpressionUrlAuthorizationConfigurer for further
         * customization
         */
        public AuthorizedUrl not() {
            this.not = true;
            return this;
        }

        /**
         * Shortcut for specifying URLs require a particular role. If you do not want to
         * have "ROLE_" automatically inserted see #hasAuthority(String).
         *
         * @param role the role to require (i.e. USER, ADMIN, etc). Note, it should not
         * start with "ROLE_" as this is automatically inserted.
         * @return the ExpressionUrlAuthorizationConfigurer for further
         * customization
         */
        public ExpressionInterceptUrlRegistry hasRole(String role) {
            return access(ExpressionUrlAuthorizationConfigurer.hasRole(role));
        }

        /**
         * Shortcut for specifying URLs require any of a number of roles. If you do not
         * want to have "ROLE_" automatically inserted see
         * #hasAnyAuthority(String...)
         *
         * @param roles the roles to require (i.e. USER, ADMIN, etc). Note, it should not
         * start with "ROLE_" as this is automatically inserted.
         * @return the ExpressionUrlAuthorizationConfigurer for further
         * customization
         */
        public ExpressionInterceptUrlRegistry hasAnyRole(String... roles) {
            return access(ExpressionUrlAuthorizationConfigurer.hasAnyRole(roles));
        }

        /**
         * Specify that URLs require a particular authority.
         *
         * @param authority the authority to require (i.e. ROLE_USER, ROLE_ADMIN, etc).
         * @return the ExpressionUrlAuthorizationConfigurer for further
         * customization
         */
        public ExpressionInterceptUrlRegistry hasAuthority(String authority) {
            return access(ExpressionUrlAuthorizationConfigurer.hasAuthority(authority));
        }

        /**
         * Specify that URLs requires any of a number authorities.
         *
         * @param authorities the requests require at least one of the authorities (i.e.
         * "ROLE_USER","ROLE_ADMIN" would mean either "ROLE_USER" or "ROLE_ADMIN" is
         * required).
         * @return the ExpressionUrlAuthorizationConfigurer for further
         * customization
         */
        public ExpressionInterceptUrlRegistry hasAnyAuthority(String... authorities) {
            return access(ExpressionUrlAuthorizationConfigurer
                    .hasAnyAuthority(authorities));
        }

        /**
         * Specify that URLs requires a specific IP Address or subnet.
         *
         * @param ipaddressExpression the ipaddress (i.e. 192.168.1.79) or local subnet
         * (i.e. 192.168.0/24)
         * @return the ExpressionUrlAuthorizationConfigurer for further
         * customization
         */
        public ExpressionInterceptUrlRegistry hasIpAddress(String ipaddressExpression) {
            return access(ExpressionUrlAuthorizationConfigurer
                    .hasIpAddress(ipaddressExpression));
        }

        /**
         * Specify that URLs are allowed by anyone.
         *
         * @return the ExpressionUrlAuthorizationConfigurer for further
         * customization
         */
        public ExpressionInterceptUrlRegistry permitAll() {
            return access(permitAll);
        }

        /**
         * Specify that URLs are allowed by anonymous users.
         *
         * @return the ExpressionUrlAuthorizationConfigurer for further
         * customization
         */
        public ExpressionInterceptUrlRegistry anonymous() {
            return access(anonymous);
        }

        /**
         * Specify that URLs are allowed by users that have been remembered.
         *
         * @return the ExpressionUrlAuthorizationConfigurer for further
         * customization
         * @see RememberMeConfigurer
         */
        public ExpressionInterceptUrlRegistry rememberMe() {
            return access(rememberMe);
        }

        /**
         * Specify that URLs are not allowed by anyone.
         *
         * @return the ExpressionUrlAuthorizationConfigurer for further
         * customization
         */
        public ExpressionInterceptUrlRegistry denyAll() {
            return access(denyAll);
        }

        /**
         * Specify that URLs are allowed by any authenticated user.
         *
         * @return the ExpressionUrlAuthorizationConfigurer for further
         * customization
         */
        public ExpressionInterceptUrlRegistry authenticated() {
            return access(authenticated);
        }

        /**
         * Specify that URLs are allowed by users who have authenticated and were not
         * "remembered".
         *
         * @return the ExpressionUrlAuthorizationConfigurer for further
         * customization
         * @see RememberMeConfigurer
         */
        public ExpressionInterceptUrlRegistry fullyAuthenticated() {
            return access(fullyAuthenticated);
        }

        /**
         * Allows specifying that URLs are secured by an arbitrary expression
         *
         * @param attribute the expression to secure the URLs (i.e.
         * "hasRole('ROLE_USER') and hasRole('ROLE_SUPER')")
         * @return the ExpressionUrlAuthorizationConfigurer for further
         * customization
         */
        public ExpressionInterceptUrlRegistry access(String attribute) {
            if (not) {
                attribute = "!" + attribute;
            }
            interceptUrl(requestMatchers, SecurityConfig.createList(attribute));
            return ExpressionUrlAuthorizationConfigurer.this.REGISTRY;
        }
    }
}

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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
参考文章
Spring Security Config : AbstractInterceptUrlConfigurer
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值