Java Server理解与实践 —— 集成Spring Web Security

引言

在开发网站的时候,通常都会面临鉴权与权限的问题。鉴权的问题更多的是关于,如何将访问网站的人鉴定为这个网站中的某个主体。而权限的问题则是关于网站中的某个主体对网站资源的权利(增删改查)。

通过支持多种鉴权模型以及对网络请求方法、方法调用甚至是领域模型的权调用权限控制,Spring Web Security满足了大部分的鉴权以及权限的需求。使用Spring web security,我们能够快速地搭建一个拥有权限管理的Java网站应用。

本文会简单介绍应当如何使用Spring Web Security,以及其背后实现的一些原理与逻辑。Spring-web-security也是基于spring-beans,spring-context以及spring-core等这些基础的spring组件的。

将Spring Web Security集成到Java Web项目

首先,我们要将相应的依赖关系添加到项目当中,这篇文章是基于4.2.2.RELEASE的。

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>4.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>4.2.2.RELEASE</version>
        </dependency>

然后,我们增加一个WebApplicationInitial类SecurityWebApplicationInitializer.java。

import org.springframework.security.web.context.*;

public class SecurityWebApplicationInitializer
    extends AbstractSecurityWebApplicationInitializer {

    public SecurityWebApplicationInitializer() {
        super(WebSecurityConfig.class);
    }
}

以及对应的security配置的类WebSecurityConfig.java。

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("user").password("password").roles("USER").build());
        manager.createUser(User.withUsername("admin").password("password").roles("ADMIN").build());
        return manager;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .authorizeRequests()
        //对于路径为/spring/hello/simple底下的请求,不进行权限验证
        .antMatchers("/spring/hello/simple/").permitAll()
        //路径为/spring/hello/admin/底下的请求,需要角色为ADMIN
        .antMatchers("/spring/hello/admin/**").hasRole("ADMIN")
        //路径为/spring/hello/user底下的请求,需要角色为USER
        .antMatchers("/spring/hello/user/**").hasRole("USER")
            .anyRequest().authenticated()
            .and()
        //支持默认表单登录以及httpBasic的鉴权方式
        .formLogin().and()
        .httpBasic();
    }
}

当完成以上配置后,所有的请求都会拥有权限管理,服务器也会生成默认的登录JSP页面。简单的配置,对原有接近0侵入的改动,是spring-web-security框架的好处之一。

未进行访问权限控制的,可以直接使用postMan获取返回结果。
这里写图片描述

使用Basic Auth进行权限验证
这里写图片描述
未经登录访问需要授权的资源,则会跳转到默认的登录页面
这里写图片描述

简单的使用示例之后,我们来看看里面的实现细节。

Spring Web Security鉴权与权限管理的实现

Spring Web Security的核心 —— SecurityFilterChain

为了尽可能的减少代码的侵入性,增强框架的灵活性,Spring web security框架使用了Servlet的filter机制进行请求的验证以及拦截。其组成部分如下图所示:
这里写图片描述
DelegatingFilterProxy持有对FilterChainProxy的引用,而FilterChainProxy则持有对多个SecurityFilterChain的引用,SecurityFilterChain也持有多个对Filter的引用。所以我们可以得知,SecurityFilterChain实际上持有了所有Spring Web Security的Filter。

那么,再具体来看SecurityFilterChain的话,在默认情况下,会有如下这些Filter被添加到其中。

Filter Class作用
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter org.springframework.security.web.csrf.CsrfFilter产生随机的csrf token,防止跨站请求伪造。
org.springframework.security.web.header.HeaderWriterFilter在响应中加入特定的header,如X-Frame-Options, X-XSS-Protection、Cache-Control 以及X-Content-Type-Options等
org.springframework.security.web.access.ExceptionTranslationFilter对异常进行翻译。
org.springframework.security.web.session.SessionManagementFilter对Session进行管理。
org.springframework.security.web.context.SecurityContextPersistenceFilter可以从持久化层中读取授权信息,也可以将授权信息持久化。默认保存到HttpSession中。
org.springframework.security.web.savedrequest.RequestCacheAwareFilter获取Request的缓存,在一次授权之后的request都可以直接使用之前的授权结果。默认是使用HttpSession进行缓存。
org.springframework.security.web.authentication.AnonymousAuthenticationFilter对于匿名用户进行授权。我们可以在Spring Web Security中设定匿名用户的权限。
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter将普通的HttpServletRequest包装成支持Secuirty的Reuqest。
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter返回登陆页面。
org.springframework.security.web.authentication.logout.LogoutFilter处理登出请求。
org.springframework.security.web.access.intercept.FilterSecurityInterceptor最后一道权限认证拦截器,如果前面的filter只进行了登陆并没有验证,则在此验证,同时,授权发生在这里(判断拥有哪些权限)。
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter使用用户名和密码进行访问的,在此进行验证。
org.springframework.security.web.authentication.www.BasicAuthenticationFilter使用BasicAuth方式访问,在此进行登陆验证。

通过以上的Filter列表我们可以知道,Spring Web Security主要做了两件事情,第一件事情是判断这个访问是否可信的(通过构造csrd token,添加响应头等方式),第二件事情就是判断这个访问是否被允许。

关于访问是否被允许,又可以细分成两部分——身份认证以及权限判断。

  • 身份认证:用于登陆的账号和密码是否存在于系统中,这个时候需要进行比对,会返回包含权限集合的认证结果。
  • 权限判断:认证结果中包含的权限,和请求所需要的权限,是否匹配。
    而这两件事情,则是由下列接口定义并且由实现这些接口的类完成的。

Spring Web Security中的身份认证与权限判断

身份认证

身份认证主要由Filter直接进行控制,其时序图如下所示:
这里写图片描述

可以看到的是,Filter里面主要做了两件事情:调用引用的AuthenticationManager进行身份认证,将身份认证的结果保存到securityContext中。为了允许并发访问,securityContext默认为ThreadLocal变量。

权限判断

这里写图片描述

权限验证发生在FilterSecurityInterceptor中。因为Authentication已经保存在SecurityContext中了,因此在获取登陆验证结果的时候,也可以直接从其中取。
通过本次对Spring Web Security框架的分析,我们了解了如何快速地实现一个网站的权限控制,同时也了解了Spring Web Security的一些特点。

小结

优点:

  • 基于Filter,使用Filter对请求进行处理,因此不会对逻辑代码有任何的侵入式改动。

  • 用户验证与用户权限判断分离,特定的Filter负责特定类型的用户登录验证,最后统一进行权限判断。

  • 可以自定义如UserDetailService等类并进行注入,比较灵活。

  • 可以自定义filter配置

  • 包含了session管理,csrf防御等基本的服务器功能

不足:

  • 中央式的session管理,不利于分布式进行扩展。

关于本次项目的代码,笔者已经上传到github上。欢迎关注。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值