暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

01-03 Spring Security

nullPoint水 2018-10-28
208

Spring Security简介

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

         SpringSecurity3.2被分为11个模块

模块

描述

ACL

支持通过访问控制列表为域对象提供安全性

切面

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

CAS客户端

提供与Jasig的中心认证服务进行集成的功能

配置

包含通过XMLjava配置Spring Security的功能支持

核心

提供Spring Security基本库

加密

提供了加密和密码编码的功能

LDAP

支持基于LDAP进行认证

OpenID

支持使用OpenID进行集中式认证

Remoting

提供了对Spring Remoting的支持

标签库

Spring SecurityJSP标签库

Web

提供了Spring Security基于FilterWeb安全性支持

         应用程序的类路径下至上要包含CoreConfiguration两个模块。

过滤Web请求

         SpringSecurity借助一系列Servlet Filter来提供各种安全性功能。DelegatingFilterProxy是一个特殊的Servlet Filter,它本身工作不多,只是将工作委托给一个javax.servlet.Filter实现类,这个实现类作为一个bean注册在Spring应用上下文中。

传统web.xml配置

<filter>
    <filter-name>
springSecurityFilterChain</filter-name>
    <filter-class>
org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

Java配置:

public class SecurityInit extends AbstractSecurityWebApplicationInitializer {}

 AbstractSecurityWebApplicationInitializer实现了WebApplicationInitializer,因此Spring会发现它,并用它在Web容器中注册DelegatingFilterProxy

两种配置方式都会拦截发往应用中的请求,并将请求委托给IDspringSecurityFilterChainbean

springSecurityFilterChain本身是另一个特殊的Filter,也被称为FilterChainProxy。可以连接任意数量的其他的Filter

Spring Security的早期,为了启用安全功能需要上百行的XML配置。Spring Security2.0提供了安全性相关的XML配置命名空间,Spring3.2引入了新的Java配置方案。如下是最简单的java配置

@Configuration
@EnableWebSecurity
//@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}

@EnableWebSecurity注解将会启用Web安全功能。但它本身没有什么用处,Spring Security必须配置在一个实现了WebSecurityConfigurerbean中或者扩展WebSecurityConfigurerAdapter。如果应用是Spring MVC开发的,可以考虑使用 @EnableWebMvcSecurity注解,区别在于@EnableWebMvcSecurity注解的还配置了一个SpringMVC参数解析的解析器,这样的话处理器方法就能够通过带有@AuthenticationPrincipal注解的参数获得认证用户的principal;同时还配置了一个bean,在使用Spring表单绑定标签库来定义表单时,这个bean会自动添加隐藏的跨站请求伪造token输入域。

为了指定Web安全的细节,可以通过重载WebSecurityConfigurerAdapter中的一个或多个方法来实现。

方法

描述

configure(WebSecurity)

通过重载,配置Spring SecurityFilter

configure(HttpSecurity)

通过重载,配置如何通过拦截器保护请求

configure(AuthenticationManagerBuilder)

通过重载,配置user-detail服务

基于上表可以得出上边的java配置Security的例子,将应用锁定了。等同于如下:

@Configuration
@EnableWebSecurity
//@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
   
@Override
   
protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic()
;
   
}
}

通过调用authorizeRequests()anyRequest().authenticated()就会要求所有进入应用的HTTP请求都要进行认证。同时由于没有重载configure(AuthenticationManagerBuilder)方法,所以没有用户存储支持认证等于没有用户,所以就是所有的请求都要求认证,但是没人能登录。

 

选择查询用户详细信息的服务

         SpringSecurity非常灵活,能够基于各种数据存储来认证用户。它内置了多种常见的用户存储场景,如内存、关系型数据库以及LDAP,而且可以程序员编写自定义的用户存储实现。

基于内存的用户存储

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

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().withUser(
"user").password("password").roles("USER").and()
            .withUser(
"admin").password("123456").roles("USER").roles("USER","ADMIN");
   
//下边与上边等价
   
auth.inMemoryAuthentication().withUser("user").authorities("ROLE_USER").and()
            .withUser(
"admin").password("123456").authorities("ROLE_USER","ROLE_ADMIN");
}

         配置用户详细信息的方法如下:

方法

描述

accountExpired(boolean)

定义账号是否已过期

accountLocked(boolean)

定义账号是否被锁定

and()

用于连接配置

authorities(grantedAuthority…)

授予某个用户一项或多项权限

authorites(List<? Extends  grantedAutority>

授予某个用户一项或多项权限

authorities()

授予某个用户一项或多项权限

credentialsExpired(boolean)

定义凭证是否过期

disabled(boolean)

定义账号是否被禁用

password(String)

定义用户密码

roles(String…)

授予某个用户一项或多项角色

 

基于数据库表进行认证

         以下是基于jdbc为支持的用户存储的最少配置

@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.jdbcAuthentication().dataSource(
dataSource);
}

         常用配置方式:

@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.jdbcAuthentication().dataSource(
dataSource).usersByUsernameQuery("select username.password,true" +
           
"from USER where username = ?").authoritiesByUsernameQuery("select username,'ROLE_USER' from USER " +
           
"where username = ?");
}

         上述例子只重写了认证和基本权限的查询语句,但是通过调用groupAuthoritiesByUsername()方法,可以将群组权限重写为自定义的查询语句。

         将默认的SQL查询替换为自定义的设计时,很重要的一点就是要遵循查询的基本协议。所有查询都将用户名作为唯一的参数。认证查询会选取用户名、密码以及启用状态信息。

使用转码后的密码

         一般数据库中存放的密码均为密文,为了解决密文的问题,需要借助passwordEncoder()方法指定一个密码转换器。

@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.jdbcAuthentication().dataSource(
dataSource).usersByUsernameQuery("select username.password,true" +
           
"from USER where username = ?").authoritiesByUsernameQuery("select username,'ROLE_USER' from USER " +
           
"where username = ?").passwordEncoder(new PasswordEncoder() {
       
@Override
       
public String encode(CharSequence charSequence) {
           
return null;
       
}

       
@Override
       
public boolean matches(CharSequence charSequence, String s) {
           
return false;
       
}
    })
;
}

         passwordEncoder()方法可以接受SpringSecurityPasswordEncoder接口的任意实现。Spring提供了一些默认实现,如:StandardPasswordEncoderBCryptPasswordEncoderAbstractPasswordEncoder等等。

基于LDAP进行认证

        @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.ldapAuthentication().userSearchFilter(
"(uid={0})").groupSearchFilter("member={0}");
}

         方法userSearchFilter()groupSearchFilter()用来为基础LDAP查询提供过滤条件,分别用于搜索用户和组。默认情况下是空的也就是从LDAP层级的根开始。

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   
// 从people的组织单元下搜索而不是从根开始,组应该在名为groups的组下搜索
   
auth.ldapAuthentication().userSearchBase("ou=people").userSearchFilter("(uid={0})")
            .groupSearchBase(
"ou=groups").groupSearchFilter("member={0}");
}

         增加密码校验,passwordEncoder()方法指定加密策略

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   
// 增加密码校验
   
auth.ldapAuthentication().userSearchBase("ou=people").userSearchFilter("(uid={0})")
            .groupSearchBase(
"ou=groups").groupSearchFilter("member={0}")
            .passwordCompare().passwordEncoder(
new Md4PasswordEncoder()).passwordAttribute("password");
}

         默认情况下,SpringSecurityLDAP认证假设LDAP服务器监听本地的33389端口。可以通过contextSource()方法来配置地址。

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   
// 配置远程的LDAP服务器
   
auth.ldapAuthentication().userSearchBase("ou=people").userSearchFilter("(uid={0})")
            .groupSearchBase(
"ou=groups").groupSearchFilter("member={0}")
            .contextSource().url(
"ldap://asdf.com:1234/dc=test,dc=com");
}

配置自定义用户对象

         有时需要认证的用户存储在非关系型数据库,或者已有的不能支持需求。Spring Security提供了一个自定义的UserDetailsService接口,只需实现该接口就可实现自定义的用户对象。

拦截请求

         重载configure(HttpSecurity)方法可以对每个请求进行细粒度的安全性控制。例如:

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity.authorizeRequests()
            .antMatchers(
"/test/aaa").authenticated()
            .antMatchers(HttpMethod.
POST,"/getInfo").authenticated()
            .anyRequest().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功能认证的,允许访问

         我们可以将以上任意数量的方法连接器满足安全需要,但是,这些规则会按照给定的顺序发挥作用,所以需要将最具体的请求路径放在前面,最不具体的放在最后面。如果不这样,不具体的路径配置会覆盖掉更具体的路径配置。

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
   
//.requiresChannel()指定是否需要安全通道并自动将请求重定向到HTTPS(HTTP)上
   
httpSecurity.authorizeRequests()
            .antMatchers(
"/test/aaa").authenticated()
            .and().requiresChannel().antMatchers(
"111").requiresSecure();
}

 

认证用户

         认证用户相关方法

@Override
protected void configure(HttpSecurityhttpSecurity) throws Exception {
   
//跳转登录页,并指定登录页
   
httpSecurity.formLogin().loginPage("/login").and()
           
//启用HTTPBasic认证。
           
.httpBasic().realmName("test").and()
           
//启用rememberme 指定失效时间 指定key  在前端页面增加remember me参数
           
.rememberMe().tokenValiditySeconds(123123).key("key").and()
           
//启用退出功能 指定退出页面
           
.logout().logoutSuccessUrl("/").logoutUrl("/signOut").and()
           .authorizeRequests().antMatchers(
"......");
}

 

保护视图

         SpringSecurity提供了一个JSP标签库,Thymeleaf通过特定的方言实现了Spring Security

         以下为SpringSecurity通过JSP标签库在视图层上支持安全性

JSP标签

作用

<security:accesscontrollist>

如果用户通过访问控制列表授予了指定的权限,那么渲染改标签体重的内容

<security:authentication>

渲染当前用户认证对象的详细信息

<security:authorize>

如果用户被授予了特定的权限或者SpEL表达式计算结果为true,那么渲染改标签体中的内容

         以下为ThymeleafSpringSecurity方言

属性

作用

sec:authentication

渲染认证对象的属性。类似于Spring Security<sec:authentication/>JSP标签

sec:authorize

基于表达式的计算结果,条件性的渲染内容。类似于Spring Security<sec:authorize/>标签

sec:authorize-acl

基于表达式的计算结果,条件性的渲染内容。类似于Spring Security<sec:accesscontrollist/>JSP标签

sec:authorize-expr

sec:authorize属性的别名

sec:authorize-url

基于给定URL路径相关的安全规则,条件性的渲染内容。类似于Spring Security<sec:authorize/>JSP标签使用url属性时的场景

 


文章转载自nullPoint水,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论