spring security

前言

本章内容:

    ▪️Spring Security介绍

    ▪️使用Servlet规范中的Filter保护Web应用

    ▪️基于数据库和LDAP进行认证

    Spring Security是为基于Spring的应用程序提供声明式安全保护的安全性框架。Spring Security提供了完整的安全性解决方案,它能够在Web请求级别和方法调用级别处理身份认证和授权。因为基于Spring框架,所以Spring Security充分利用了依赖注入(dependency injection,DI)和面向切面的技术。

    最初,Spring Security被称为Accegi Security。到了2.0版本,Accegi Security更名为Spring Security。但是2.0发布版本所带来的不仅仅是表面上名字的变化。为了在Spring中配置安全性,Spring Security引入了一个全新的、与安全性相关的XML命名空间。这个新的命名空间连同注解和一些合理的默认设置,将典型的安全性配置从几百行XML减少到几十行。Spring Security 3.0融入了SpEl,这进一步简化了安全性的配置。

    它的最新版本为3.2,Spring Security从两个角度来解决安全性问题。它使用Servlet规范中的Filter保护Web请求并限制URL级别的访问。Spring Security还能够使用Spring AOP保护方法调用---借助于对象代理和使用通知,能够确保只有具备适当权限的用户才能访问安全保护的方法。

    在本章中,我们将会关注如何将Spring Security用于Web层的安全性之中。在稍后的第14章中,我们会重新学习Spring Security,了解它如何保护方法的调用。

理解Spring Security的模块

    不管你想使用Spring Security保护哪种类型的应用程序,第一件需要做的事就是将Spring Security模块添加到应用程序类路径下。Spring Security 3.2分为11个模块,如表9.1所示:

    ▪️ACL:支持通过访问控制列表(access contol list,ACL)为域对象提供安全性。

    ▪️切面(Aspects):一个很小的模块,当使用Spring Security注解时,会使用基于AspectJ的切面,而不是使用标准的Spring AOP。

    ▪️CAS客户端(CAS Client):提供与Jasig的中心认证服务(Central Authentication Service,CAS)进行集成的功能。

    ▪️配置(Configuration):包含通过XML和Java配置Spring Security的功能支持。

    ▪️核心(Core):提供Spring Security基本库。

    ▪️加密(Cryptography):提供了加密和密码编码的功能。

    ▪️LDAP:支持基于OpenID进行集中式认证。

    ▪️Remoting:提供了对Spring Remoting的支持。

    ▪️标签库(Tag Library):Spring Security的JSP标签库。

    ▪️Web:提供了Spring Security基于Filter的Web安全性支持。

    应用程序的类路径下至少要包含Core和Configuration这两个模块。Spring Security经常被用于保护Web应用,这显然也是Spittr应用的场景,所以我们还需要添加Web模块。同时我们还会用到Spring  Security的JSP标签库,所以我们需要将这个模块也添加进来。

    现在,我们已经为在Spring Security中进行安全性配置做好了准备。让我们看看如何使用Spring Security的XML命名空间。

过滤Web请求

    Spring Security借助一系列Servlet Filter来提供各种安全性功能。你可能会想,这是否意味着我们需要在web.xml或WebApplicationInitializer中配置多个Filter呢?实际上,借助于Spring的小技巧,我们只需配置一个Filter就可以了。

    DelegatingFilterProxy是一个特殊的Servlet Filter,它本身所做的工作并不多。只是将工作委托给一个javax.servlet.Filter实现类,这个实现类作为一个注册在Spring应用的上下文中,如图9.1所示。

图 9.1 DelegatingFilterProxy把Filter的处理逻辑委托給Spring应用上下文中所定义的一个代理Filter bean

如果你喜欢在传统的web.xml中配置Servlet和Filter的话,可以使用元素,如下所示:

pom.xml

    在这里,最重要的是设置成了SpringSecurityFilterChain。这是因为我们马上就会将Spring Security配置在Web安全性之中,这里会有一个名为SpringSecurityFilterChain的Filter bean,DelegatingFilterProxy会将过滤逻辑委托给它。

    如果你希望借助WebApplicationInitializer以Java的方式来配置DelegatingFilterProxy的话,那么我们所需要做的就是创建一个扩展的新类:

WebApplicationInitializer

    AbstractSecurityWebApplicationInitializer实现了WebApplicationInitializer,因此Spring会发现它,并用它在Web容器中注册DelegatingFilterProxy。尽管我们可以override它的appendFilters()或insertFilters()来注册资金选择的Filter,但是要注册DelegatingFilterProxy的话我们并不需要重载任何方法。

    不管我们通过web.xml还是通过AbstractSecurityWebApplicationInitializer的子类来配置DelegatingFilterProxy,它都会拦截发往应用中的请求,并将请求委托给ID为springSecurityFilterChain的bean。

    springSecurityFilterChain本身是另一个特色的Filter,它也被称为FilterChainProxy。它可以链接任意一个或多个其他的Filter。Spring Security依赖一系列Servlet Filter来提供不同的安全特性。但是,你几乎不需要知道这些细节,因为你不需要显式声明springSecurityFilterChain以及它所链接在一起的其他Filter。当我们启用Web安全性的时候,会自动创建这些Filter。

为了让Web安全性运行起来,我们创建一个简单的安全性配置。

编写简单的安全性

    在Spring Security的早期版本中(在其还被称为Accegi Security之时),为了在Web应用中启用简单的安全功能,我们需要编写上百行的XML配置。Spring Security 2.0提供了安全性相关的XML配置命名空间,让情况有了一些好转。

    Spring 3.2引入了新的Java配置方案,完全不再需要通过XML来配置安全性功能了。如下的程序清单展现了Spring Security最简单的Java配置。

程序清单9.1

    顾名思义,@EnableWebSecurity注解将会启用Web安全功能。但它本身并没有什么用处,Spring Security必须配置在一个实现了WebSecurityConfugurer的bean中,或者(简单起见)扩展WebSecurityConfigurerAdater。在Spring应用上下文中,任何实现了WebSecurityConfigurer的bean都可以用来配置Spring Security,但是最为简单的方式还是像程序清单9.1那样扩展WebSecurityConfigurerAdapter类。

    @EnableWebSecurity可以启用任意Web应用的安全性功能,不过,如果你的应用碰巧是使用Spring MVC开发的,那么就应该考虑使用@EnableWebMvcSecurity替代它,如程序清单9.1所示。

程序清单9.2

    除了其他的内容以外,@EnableWebMvcSecurity注解还配置了一个Spring MVC参数解析器(argument resolver),这样的话处理器方法就能够通过带有@AuthenticationPrincipal注解的参数获得认证用户的principal(或username)。它同时还配置了一个bean,在使用Spring表单绑定标签库来定义表单时,这个bean会自动添加一个隐藏的跨站请求伪造(cross-site request forgery,CSRF)token输入域。

    看起来似乎并没有做太多的事情,但程序清单9.1和9.2中的配置类会给应用产生很大的影响。其中任何一种配置都会将应用严格锁定,导致没有人能够进入该系统了!

    尽管部署严格要求的,但是我们可能希望指定Web安全的细节,这要通过override WebSecurityConfigurerAdapter中的一个或多个方法来实现。我们可以通过override WebSecurityConfigurerAdapter的三个configure()方法来配置Web安全性,这个过程中会使用传递进来的参数设置行为。表9.2描述了这三个方法。

三个configure

    ▪️configure(WebSecurity):通过override,配置Spring Security的Filter链。

    ▪️configure(HttpSecurity):通过override,配置如何通过拦截器保护请求。

    ▪️configure(AuthenticationManagerBuilder):通过override,配置user-detail服务。

    让我们重新看一下程序清单9.2,可以看到它没有override上述三个configure()方法中的任何一个,这就说明了为什么应用现状是被锁定的。尽管对于我们的需求来讲默认的Filter链是不错的,但是默认的configure(HttpSecurity)实际上等同于如下所示。

默认实现

    这个简单的默认配置指定了该如何保护HTTP请求,以及客户端认证用户的方案。通过调用authorizeRequests()和anyRequest().authenticated()就会要求所有进入应用的HTTP请求都要进行认证。它也配置Spring Security支持基于表单的登录以及HTTP Basic方式的认证。

    同时,因为没有override configure(AuthenticationManagerBuilder)方法,所以没有用户存储支持认证过程。没有用户存储。实际上就等于没有用户。所以,在这里所有的请求都需要认证,但是没有人能够登录成功。

为了让Spring Security满足我们应用的需求,还需要再添加一点配置。具体来讲,我们需要:

    ▪️配置用户存储;

    ▪️指定哪些请求需要认证,哪些请求不需要认证,以及所需要的权限;

    ▪️提供一个自定义的登录页面,替代原来简单的默认登录页。

    除了Spring Security的这些功能,我们可能还希望基于安全限制,有选择性地在Web视图上显示特定的内容。

但首先,我们看一下如何在认证的过程中配置访问用户数据的服务。

选择查询用户详细的服务

    我们所需要的是用户存储,也就是用户名、密码以及其他信息存储的地方,在进行认证决策的时候,会对其进行检索。

    好消息是,Spring Security非常灵活、能够基于各种数据存储来认证用户,在进行了多种常见用户存储场景,如内存、关系型数据库以及LDAP。但我们也可以编写并插入自定义的用户存储实现。

    借助Spring Security的Java配置,我们能够很容易地配置一个或多个数据存储方案。那我们就从最简单的开始:在内存中维护用户存储。

使用基于内存的用户存储

    因为我们的安全配置扩展了WebSecurityConfigurerAdapter,因此配置用户存储的最简单方式就是overrideconfigure()方法,并以AuthenticationManagerBuilder作为传入参数。AuthenticationManagerBuilder有多个方法可以用来配置Spring Security对认证的支持。通过inMemoryAuthentication()方法,我们可以启用、配置并任意填充基于内存的用户存储。

    例如,在如程序清单9.3中,SecurityConfig override了configure()方法,并使用两个用户来配置内存用户存储。

程序清单9.3 使用内存用户存储

操作补充:

测试接口
调用测试

    我们可以看到configure()方法中的AuthenticationManagerBuilder使用构造者风格的接口来构建风格的接口来构建认证配置。通过简单地调用inMemoryAuthentication()就能启用内存用户存储。但是我们还需要有一些用户,否则的话,这和没有用户并没有什么区别。

源码

    因此,我们需要调用withUser()方法返回的是UserDetailsManagerConfigurer.UserDetailsBuilder,这个对象提供了多个进一步配置用户的方法,包括设置用户密码的password()方法以及为给定用户授予一个或多个角色权限的roles()方法。

    在代码清单9.3中,我们添加了两个用户,“user”和“admin”,密码均为“password”。“user”用户具有USER角色,而“admin”用户具有ADMIN和USER两个角色。我们可以看到,and()方法能够将多个用户的配置连接起来。

    除了password()、roles()和and()方法以外,还有其他的几个方法可以用来配置内存用户存储中的用户信息。以下描述了UserDetailsManagerCondigurer.UserDetailsBuilder对象所有可用的方法。

            方法                                                描述

accountExpired(boolean)                定义账号是否已经过期

accountLocked(boolean)                定义账号是否已经锁定

and()                                                    用来连接配置

authorities(GrantedAuthority...)    授予某个用户一项或多项权限

authorities(List)                                授予某个用户一项或多项权限

authorities(String...)                        授予某个用户一项或多项权限

credentialsExpired(boolean)           定义凭证是否已经过期

disabled(boolean)                             定义账号是否已被禁用

password(String)                             定义用户的密码

username(String)                             定义用户的用户名

roles(String...)                                 授予某个用户一项或多项角色

源码

    对于调试和开发人员测试来讲,基于内存的用户存储是很有用的,但是对于生成级别的应用来讲,这就不是最理想的可选方案了。为了用于生产环境,通常最好讲用户数据报错在某种类型的数据库之中。

基于数据库表进行认证

    用户数据通常会存储在关系型数据库中,并通过JDBC进行访问。为了配置Spring Security使用以JDBC为支撑的用户存储,我们可以使用jdbcAuthentication()方法,所需的最少配置如下所示:

使用jdbcAuthentication()方法

    我们必须要配置的只是一个DataSource,这样的话,就能访问关系型数据库了。在这里,DataSource是通过自动装配的技巧得到的。

重写默认的用户查询功能

    尽管默认的最少配置能够让一切运转起来,但是它对我们的数据库模式有一些要求,它预期存在某些存储用户数据的表。下面的代码片段来源于Spring Security内部,这块代码展现了当查找用户信息时所执行的SQL查询语句:

默认sql

    在第一个查询中,我们获取了用户的用户名、密码以及是否启用的信息,这些信息会用来进行用户认证。接下来的查询查找了用户所授予的权限,用来进行鉴权,最后一个查询中,查找了用户作为群组成员所授予的权限。

补充:源码中的建表SQL

建表sql

    如果你能够在数据库中定义和填充满足这些查询的表,那么基本上就不需要你再做什么额外的事情了。但是,也有可能你的数据库与上面所述并不一致,那么你就会希望在查询上有更多的控制权。如果是这样的话,我们可以按照如下的方式配置自己的查询:

数据库查询

    在本例中,我们只重写了认证和基本权限的查询语句,但是通过调用groupAuthoritiesByUsername()方法,我们也能够讲群组权限重写为自定义的查询语句。

    将默认的SQL查询替换为自定义的设计时,很重要的一点就是要遵循查询的基本协议。所有查询都将用户名作为唯一的参数。认证查询会选取用户名、密码以及启用状态信息。权限查询会选取零行或多行数据,每行数据中都会包含群组ID、群组名称以及权限。

补充:

权限表
user 表


程序启动
调用测试

使用转码后的密码

    看一下上面的认证查询,它会预期用户密码存储在了数据库之中。这里唯一的问题在于如果密码明文存储的话,会很容易受到黑客的窃取。但是,如果数据库中的密码进行了转码的话,那么认证就会失败,因为它与用户提交的明文密码并不匹配。

    为了解决这个问题,我们需要借助passwordEncoder()方法指定一个密码转码器(encoder)。

密码加密

    passwordEncoder()方法可以接受Spring Security中PasswordEncoder接口的任意实现。Spring Security的加密模块包括了三个这样的实现:BCryptPasswordEncoder、NoOpPasswordEncoder和StandardPasswordEncoder。

    上述的代码中使用了StandardPasswordEncoder,但是如果内置的实现无法满足需求时,你可以提供自定义的实现。PasswordEncoder接口非常简单:

源码

    不管你使用哪一个密码转码器,都需要理解的一点是,数据库中的密码是永远不会解码的。所采取的策略与之相反,用户在登录时输入的密码会按照相同的算法进行转码,然后再与数据库中已经转码过的密码进行对比。这个比对是在PasswordEncoder的matchers()方法中进行的。

配置自定义的用户服务

    假设我们需要认证的用户存储在非关系型数据库中,如Mongo或Neoj4,在这种情况下,我们需要提供一个自定义的UserDetailsService接口实现。

UserDetailsService接口非常简单:

UserDetailsService接口

    我们所需要做的就是实现loadUserByUsername()方法,根据给定的用户名来查找用户。loadUserByUsername()方法会返回代表给定用户的UserDetails对象。如下的程序清单展现了一个UserDetailsService的实现,它会从给定的SpitterRepository实现中查找用户。

程序清单9.4 从数据库中查找UserDetails对象

    UserServiceImpl有意思的地方在于它并不知道用户数据存储在什么地方。设置进来的userDao能够从关系型数据库,文档数据库或图数据中查找UserEntry对象,甚至可以伪造一个。UserServiceImpl不知道也不会关心底层所使用的数据存储。它只是获得UserEntry对象,并使用它来创建User对象。(User是UserDetails的具体实现)。

    为了使用UserServiceImpl来认证用户,我们可以通过userDetailsService()方法将其设置倒安全配置中:

UserServiceImpl来认证用户

    userDetailsService()方法(类似于jdbcAuthentication()、ldapAuthentication()以及inMemoryAuthentication())会配置一个用户存储。不过,这里所使用的不是Spring所提供的用户存储,而是使用UserDetailsService的实现。

    另外一个值得考虑的方案就是修改UserEntry,让其实现UserDetails。这样的话loadUserByUsername()就能直接返回UserEntry对象了,而不必再将它的值复制到 User对象中。

拦截请求

    在前面的9.1.3小节中,我们看到一个特别简单的Spring Security配置,在这个默认的配置中,回要求所有请求都要经过认证。有些人可能会说,过多的安全性总比安全性太少要好。但也有一种说法就是要适量地应用安全性。

    在任何应用中,并不是所有的请求都需要同等程度地保护。有些请求需要认证,而另一些可能并不需要。有些请求可能只有具备特定权限的用户才能访问,没有这些权限的用户无法访问。

    例如,考虑Spittr应用的请求。首先当然是公开的,不需要进行保护。类似地,因为所有的Spittle都是公开的,所以展现Spittle的页面不需要安全性。但是,创建Spittle的请求只有认证用户才能知晓。同样,尽管用户基本信息页面是公开的,不需要认证,但是,如果要处理“/spitter/me”请求,并展现当前用户的基本信息时,那么就需要进行认证,从而确定要展现谁的信息。

    对每个请求进行细粒度安全性控制的关键在于overrideconfigure(HttpSecurity)方法。如下的代码片段展现了override的configure(HttpSecurity)方法,它为不同的URL路径有选择地应用安全性:

overrideconfigure(HttpSecurity)方法

    configure()方法中得到的HttpSecurity对象可以在多个方面配置HTTP的安全性。在这里,我们首先调用authorizeRequests(),然后调用该方法所返回的对象的方法来配置请求级别的安全性细节。其中,第一次调用antMatchers()更为具体,说明对"security"路径的请求需要进行认证。第二次调用antMatchers()更为具体,说明对"/spittle"路径的HTTP POST请求必须要经过认证。最后对anyRequests()的调用中,说明其他所有的请求都是允许的,不需要认证和任何的权限。

    antMatchers()方法中设定的路径支持Ant风格的通配符。在这里我们并没有这样使用,但是也可以使用通配符来指定路径,如下所示:

        .antMatchers("/security/**").authenticated();

我们也可以在一个对antMatchers()方法的调用中指定多个路径:

        .antMatchers("/security/**","/security/test").authenticated();

    antMatchers()方法所使用的路径可能会包括Ant风格的通配符,而regexMatchers()方法则能够接受正则表达式来定义请求路径。例如,如下代码片段所使用的正则表达式与"/security/**"(Ant风格)功能是相同的:

           .regexMatchers("/security/.**").authenticated();

    除了路径选择,我们还通过authenticated()和permitAll()来定义该如何保护路径。authenticated()要求在执行该请求时,必须已经登录了应用。如果用户没有认证时,permitAll()方法允许请求没有任何的安全限制。

    除了authenticated()和permitAll()以外,还有其他的一些方法能够涌来定义该如何保护请求,如下表描述了所有可用的方案。

                            定义如何保护路径的配置方法

方法                                                        能够做什么

access(String)                                      如果给定的SpEL表达式计算结果为true,就允许访问

anonymous()                                        允许匿名用户访问

authenticated()                                    允许经过认证的用户访问

denyAll()                                                无条件拒绝所有访问

fullyAuthenticated()                            如果用户是完整的话(不是通过Remember-me功能认证的),就允许访问

hasAnyAuthority(String...)                 如果用户具备给定权限中的某一个的话,就允许访问

hasAnyRole(String...)                          如果用户具备给定角色中的某一个的话,就允许访问

hasAuthority(String)                            如果用户具备给定权限的话,就允许访问

hasIpAddress(String)                           如果请求来自给定IP地址的话,就允许访问

hasRole(String)                                     如果用户具备给定角色的话,就允许访问

not()                                                         对其他访问方法的结果球反

permitAll()                                              无条件允许访问

rememberMe()                                      如果用户是通过Remember-me功能认证的,就允许访问

    通过使用上表中的方法,我们所配置的安全性能够不仅仅限于认证用户。例如,我们可以修改之前的configure()方法,要求用户不仅需要认证,还要具备ROLE_SECURITY权限:    

hasAuthority()方法

作为替代方案,我们还可以使用hasRole()方法,它会自动适应“ROLE_”前缀:

hasRole()方法

    我们可以将任意数量的antMatchers()、regexMatchers()和anyRequest()连起来,以满足Web应用安全规则的需要。但是,我们需要知道,这些规则会按照给定的顺序发挥作用。所以说,很重要的一点就是将最为具体的请求路径放在前面,而最不具体的路径(anyReuquest())放在最后面。如果不这样的话,那部具体的路径配置将会覆盖掉更为具体的路径配置。

使用Spring表达式进行安全保护

    上表中的大多数法官法都是一维的,也就是说我们可以使用hasRole()限制某个特定的角色,但是我们不能在相同的路径上同时通过hasIpAddress()限制特定的IP地址。

    另外,除了上表定义的方法以外,我们没有方法使用其他的条件。如果我们希望限制某个角色智能在星期二进行访问的话,该怎么办呢?

    在第3章中,我们看到了如何使用Spring表达式语言(Spring Expression Language,SpEl),将其作为装配bean属性的高级技师。借助access()方法,我们也可以将SpEL作为声明访问限制的一种方式。例如,如下就是使用SpEL表达式来声明声明具有“ROLE_SECURITY”角色才能访问"/security/test"URL:

                .antMatchers("/security/test").access("hasRole('ROLE_SECURITY')")

    这个对"/security/test"的安全限制与开始时代效果时等价的,只不过这里使用了SpEL来描述安全规则。如果当前用户呗授予了给定角色的话,那hasRole()表达式的计算结果就为true。

    让SpEL更强大的原因在于,hasRole()仅是Spring支持的安全相关表达式中的一种,下表列出了Spring Security支持的所有SpEL表达式。

            Spring Security通过一些安全性相关的表达式扩展了Spring表达式语言

安全表达式                                              计算结果

authentication                                      用户的认证对象

denyAll                                                   结果始终为false

hasAnyRole(list of roles)                    如果用户被授予了列表中任意的指定角色,结果为true

hasRole(role)                                        如果用户被授予了指定的角色,结果为true

hasIpAddress(IP Address)                 如果请求来自指定IP的话,结果为true

isAnonymous()                                     如果当前用户为匿名用户,结果为true

isAuthenticated()                                 如果当前用户进行了认证的话,结果为true

isFullyAuthenticated()                        如果当前用户进行了完整认证的话(不是Remember-me),结果为true

isRememberMe()                                 如果当前用户是通过Remember-me自动认证的,结果为true

permitAll                                               结果始终为true

principal                                                用户的principal对象

    在掌握了Spring Security的SpEL表达式后,我们就能够不再局限于机遇用户的权限逆袭访问限制了。例如,如果你想限制"/security/test" URL的访问,不仅需要ROLE_SECURITY,还需要来自指定的IP地址,那么我们可以按照如下的方式调用access()方法。

access()方法

    我们可以使用SpEL实现各种各样的安全性限制。我敢打赌,你已经在想象基于SpEL所能实现的那些有趣的安全性限制了。

但现在,让我们看一下Spring Security拦截请求的另外一种方式:强制通道的安全性。

强制通道的安全

    使用HTTP提交数据是一件具有风险的事情。如果使用HTTP发送无关紧要的信息,这可能不是什么大问题。但是如果你通过HTTP发送诸如密码和信用卡号这样的敏感信息的话,那你就是在找麻烦了。通过HTTP发送的数据没有经过加密,黑客就有机会拦截请求并且能够看到他们想看的数据。这就是为什么敏感信息要通过HTTPS来加密发送的原因。

    使用HTTPS似乎很简单。你要的事情知识在URL中的HTTP后加上一个字母“s”就可以了。是这样吗?

    这是真的,但这是把使用HTTPS通道的责任放在了错误的地方。通过添加“s”我们就能很容易地实现页面的安全性,但是忘记添加“s”同样也是很容易出现的。如果我们的应用中有多个链接需要HTTPS,估计在其中的一两个上忘记添加“s”的概率还是很高的。

另一方面,你可能还会在原本并步需要HTTPS的地方,误用HTTPS。

    传递到configure()方法中的HttpSecurity对象,除了具有authorizeRequests()方法以外,还有requiresChannel()方法,借助这个方法能够为各种URL模式声明所要求的通道。

    作为示例,可以参考security应用的注册表单。尽管security应用步需要信用卡号、社会保障好或其他敏感的信息,但用户有可能仍然希望信息是私密的。为了保证注册表单的数据通过HTTPS传递,我们可以在配置中添加requiresChannel()方法,如下所示:

添加requiresChannel()方法

    不论何时,只要是对"/security/form"的请求,Spring Security都视为需要安全通道(通过调用requiresChannel()确定的)并自动将请求重定向到HTTPS上。

    与之相反,有些页面并不需要通过HTTPS传送。例如,首页包含任何敏感信息,因此并步需要通过HTTPS传送。我们可以使用requiresInsecure()代替requiresSecure()方法,将首页声明为始终通过HTTP传送:

                    .antMatchers("/").requiresInsecure();

    如果通过HTTPS发送了对“/”的请求,Spring Security将会把请求重定向到不安全的HTTP通道上。

    在强制要求通道时,路径的选取方案与authorizeRquests()是相同的。使用了antMatchers(),但是我们也可以使用regexMatchers()方法,通过正则表达式选取路径模式。

使用HTTP Basic认证

    对于应用程序的人类用户来说,基于表单的认证是比较理想的。但是在第16章中,将会看到如何将我们Web应用的页面转化为RESTful API。当应用程序的使用者是另外一个应用程序的话,使用表单来提示登录的方式就不太合适了。

    HTTP Basic认证(HTTP Basic Authentication)会直接通过HTTP请求本书,对药访问应用程序的用户进行认证。你可能在以前见过HTTP 401响应,表明必须要在请求中包含一个用户名和密码。在REST客户端向它使用的服务进行认证的场景中,这种方式比较合适。

    如果要启用HTTP Basic认证的话,只需在configure()方法所传入的HttpSecurity对象调用httpBasic()即可。另外,还可以通过调用realmName()方法指定域。如下是在Spring Security中启用HTTP Basic认证的典型配置:

HTTP Basic认证

    注意:和前面意义,在configure()方法中,通过调用and()方法来将不同的配置指令连接在一起。

spring security 动态配置url权限

    这里我们要操作的是FilterSecurityInterceptor这个interceptor,使用withObjectPostProcessor来设置。

这个filters有几个要素,如下:

        ▪️SecurityMetadataSource

         ▪️AccessDecisionManager

        ▪️AuthenticaManager

    可以根据情况自己去重新设置,这里我们重写一下SecurityMetadataSource用来动态获取url权限配置,还有AccessDecisionManager来进行权限判断。

    这里遍历判断url所需的角色看用户是否具备,有具备则返回,都不具备则AccessDeniedException异常。

MyFilterInvocationSecurityMetadataSource

MyFilterInvocationSecurityMetadataSource

    这里以内存的map来展示一下,实际应用可以从分布式配置中心或者数据库中读区,另外循环遍历这个可能很消耗性能,必要时得优化一下。

MySecurityConfig

MySecurityConfig

异常记录

▪️异常:IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"

      spring security 5的密码存储格式改了,要求我们提供一个密码编码器。我们必须进行如下配置,数据库保存的密码必须是加密之后的。

添加密码编码器

如果需要給我修改意见的发送邮箱:erghjmncq6643981@163.com

本博客的代码示例已上传GitHub:分布式配置中心

资料参考:《Spring in cation》,《Spring Cloud 微服务实战》

转发博客,请注明,谢谢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,230评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,261评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,089评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,542评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,542评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,544评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,922评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,578评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,816评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,576评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,658评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,359评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,920评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,859评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,381评论 2 342

推荐阅读更多精彩内容