spring security

作者: Chandler_珏瑜 | 来源:发表于2018-03-16 17:48 被阅读1114次

    前言

    本章内容:

        ▪️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 微服务实战》

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

    相关文章

      网友评论

      本文标题:spring security

      本文链接:https://www.haomeiwen.com/subject/aadrqftx.html