两个示例来感受Spring Security认证的灵活性

本文来自于公众号链接:  两个示例来感受Spring Security认证的灵活性

公众号中文章格式更加良好,干货文章都在:码闻

两个示例来感受Spring Security认证的灵活性_第1张图片

这是一篇理论和实战相结合的干货文章,建议手机阅读者收藏,并且使用电脑下载源码进行实战

大纲:

一.概述

二.源码

三.示例1:接口分多组,每组认证方式不同

四.示例2:同组接口同时支持多种认证

五.总结

六.参考

一.概述

很多朋友虽然使用Spring Security做过很多项目,但是总是感觉没有理解和掌握Spring Security的核心思想。Spring Security比Shiro更强大更灵活的同时,确实带来了一定的复杂性。然而这种复杂性并不是因为Spring Security设计得不好造成的,而是因为安全需求本身就像一团乱麻一样错综复杂,Spring Security成功地将这团乱麻梳理柔顺后呈现给大家。可以说,在解决复杂安全问题的场景下,Spring Security已经足够简洁了。我们更应该关注Spring Security的灵活性。

认证、鉴权和漏洞防御是Spring Security的核心功能,其中的认证功能非常强大并且非常灵活。Spring Security支持的认证方式在不断地水平扩展,已经预置了非常丰富的认证方式,如:

Form认证: 最常用的是用户名/密码认证方式。有很多变体,如(手机号/验证码)认证方式

HTTP BASIC认证

LDAP认证: 大型环境如企业用户经常采用LDAP

CAS认证 :单点登录场景下常用解决方案

HTTP Digest 认证( IETF RFC-based 标准)

HTTP X.509 客户端证书交换 ( IETF RFC-based 标准)

OpenID 认证

......

如果预置的认证方式仍然不满足需求,Spring Security支持用户自定义认证方式。

除了认证方式的丰富性,Spring Security对认证的配置也非常灵活,主要体现在Spring Security支持多种认证方式的自由组合

本文主要通过两个代码示例来展示Spring Security“多种认证方式的自由组合”的灵活性:

第一个配置示例: 接口分多组,每组认证方式不同

第二个配置示例: 同组接口同时支持多种认证

二.源码

本文源码维护在Github,非常具有参考价值,建议下载源码:

https://github.com/andyzhaozhao/spring-security-url-separate

有两个模块:

两个示例来感受Spring Security认证的灵活性_第2张图片

spring-security-url-separate-sample: 本文展示Spring Security认证的灵活性的配置示例。

authorizationserver:自建的OAuth2认证服务器。

三.示例1:接口分多组,每组认证方式不同

假设一个web应用的一部分接口是暴露在互联网上提供服务API接口,而另一部分接口是只需在内部访问的内部接口。我们常常把这个web应用配置为OAuth2资源服务器,其中API接口采用OAuth2资源服务认证,而内部接口则采用传统的认证方式,如Form认证。

Spring Security支持这种“接口分多组,每组认证方式不同”类型的需求。

1.新建Spring Boot工程

新建Spring Boot工程,命名为“spring-security-url-separate-sample”,依赖:

    org.springframework.boot

    spring-boot-starter-security

    org.springframework.boot

    spring-boot-starter-oauth2-resource-server

    org.springframework.boot

    spring-boot-starter-web

spring-boot-starter-oauth2-resource-server是OAuth2资源服务器功能包。

2.增加两个Controller

FooController:

@RestController

public class FooController {

    @GetMapping("/foo")

    public String getSample() {

        return "get foo";

    }

}

BarController:

@RestController

public class BarController {

    @GetMapping("/bar")

    public String getSample() {

        return "get bar";

    }

}

两个Controller用来模拟两组API接口。

3.两个Spring Security配置类

GroupOneSecurityConfig配置所有以“/foo”开头的接口使用OAuth2资源服务进行认证和鉴权:

@Configuration

//此处配置Order=1,因而比 GroupTwoSecurityConfig 配置类先执行,配置优先级高,因为Spring Security的配置规则是"先执行的优先级高"。

@Order(1)

public class GroupOneSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override

    public void configure(HttpSecurity http) throws Exception {

        http.requestMatchers()

                .antMatchers("/foo/**")

                .and().authorizeRequests().anyRequest().fullyAuthenticated()

                .and().oauth2ResourceServer().jwt();

    }

}

GroupTwoSecurityConfig配置除了以“/foo”开头的接口外剩余的其他接口使用From认证:

@EnableWebSecurity(debug = true)

public class GroupTwoSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override

    public void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()

                .antMatchers("/error", "/login**").permitAll()

                .anyRequest().fullyAuthenticated()

                .and().formLogin();

    }

    @Override

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.inMemoryAuthentication()

                .withUser("admin")

                .password(passwordEncoder().encode("123456"))

                .roles("ADMIN", "USER");

    }

    @Bean

    public PasswordEncoder passwordEncoder() {

        return new BCryptPasswordEncoder();

    }

}

GroupTwoSecurityConfig类配置的接口选择范围为“所有接口”,包含了GroupOneSecurityConfig选择的以“/foo”开头的接口,但是不用担心两个配置类的范围冲突问题,因为我们在 GroupOneSecurityConfig上增加了@Order(1)注解。@Order表示执行优先级,WebSecurityConfigurerAdapter默认的优先级是100:

两个示例来感受Spring Security认证的灵活性_第3张图片

Spring Security的@Order规则是:“数字越小,越先执行,并且先执行的配置优先级高”。@Order(1)小于默认的@Order(100),所以GroupOneSecurityConfig的接口选择“/foo”的优先级高、先生效,而GroupTwoSecurityConfig只能选择到除“/foo”以外的剩余接口。

至此,工程的接口分为了两组:

以“/foo”开头的接口,使用OAuth2资源服务认证

除“/foo”开头接口外剩余的接口,使用From认证。

注意GroupTwoSecurityConfig要配置“/error”和“/login**”为permitAll()

4.配置文件

server:

  servlet:

    session:

      cookie:

        name: UISESSIONCOUPON

spring:

  security:

    oauth2:

      resourceserver:

        jwt:

          jwk-set-uri: http://localhost:9999/.well-known/jwks.json

自建OAuth2认证服务器和本工程都以“localhost”启动,配置不同的cookie名称,防止冲突报错。使用JWK接口验证 JWT Token 的有效性。

5.运行与演示

假设spring-security-url-separate-sample是一个在互联网提供API接口的服务器,其中“/foo”接口暴露在互联网提供服务的API接口,而 “/bar”接口为内部接口,则有如下访问场景:

1.内部接口可以直接使用Form认证

启动“spring-security-url-separate-sample”

隐身模式打开浏览器,输入:http://localhost:8080/bar

两个示例来感受Spring Security认证的灵活性_第4张图片

输入用户名/密码:admin/123456,可以登录成功

浏览器再输入:http://localhost:8080/foo ,也可以成功返回字符串“get foo”。 用户使用Form登录后既可以访问API接口也可以访问内部接口

2.暴露在互联网的API接口只能使用OAuth2资源服务认证

因为随便来自于互联网的用户不能获得内部的用户名和密码,但是可以获得OAuth2统一认证服务的用户名和密码。

启动“authorizationserver”

启动“spring-security-url-separate-sample”

隐身模式打开浏览器,输入:http://localhost:8080/foo  

未认证时,OAuth2资源服务认证不会自动重定向到认证中心,而是显示要401错误。调用 “/foo”接口是需要携带OAuth2的access token的。

两个示例来感受Spring Security认证的灵活性_第5张图片

使用curl命令行工具模拟OAuth2请求,使用密码模式获取一个access token:

curl -i -X POST -d"username=admin&password=admin&grant_type=password&client_id=client-for-server&client_secret=client-for-server"http://localhost:9999/oauth/token

两个示例来感受Spring Security认证的灵活性_第6张图片

携带access_token请求“/foo”接口:

curl -i -H"Authorization:Bearer {此处粘贴上一步骤返回的access_token}"http://localhost:8080/foo

两个示例来感受Spring Security认证的灵活性_第7张图片

外部用户成功调用“/foo”接口,但是仍然无法调用到内部“/bar”接口。内部接口”被成功地保护起来了

此示例将应用接口分为两组,分别使用不同的认证方式。理论上Spring Security可以将接口分为任意多组,使用任意多种认证方式,非常灵活。

四.示例2:同组接口同时支持多种认证

除了多组接口使用不同的认证方式外。对于同一组接口,可以同时支持多种认证方式。

我们继续对“spring-security-url-separate-sample”进行扩展。

1.增加OAuth2 Client依赖

    org.springframework.boot

    spring-boot-starter-oauth2-client

2.在GroupTwoSecurityConfig中增加配置

@Override

public void configure(HttpSecurity http) throws Exception {

  ...

  .and().formLogin()

  .and().oauth2Login();

}

对于除“/foo"以外的其他接口同时支持Form认证和OAuth2客户端认证。

3.配置文件增加配置

spring:

  security:

    oauth2:

      client:

        registration:

          github:

            client-id: b7fb29a538bb19b09365

            client-secret: 2fbfd22e69e61d873bad55143538770748a76d3a

          custom:

            client-id: client-for-server

            client-secret: client-for-server

            provider: custom

            client-name: 自建OAuth2认证服务

            authorization-grant-type: authorization_code

            redirect-uri: "http://localhost:8080/login/oauth2/code/custom"

        provider:

          custom:

            authorization-uri: http://localhost:9999/oauth/authorize

            token-uri: http://localhost:9999/oauth/token

            user-info-uri: http://localhost:9999/me

            user-name-attribute: "name"

            jwk-set-uri: http://localhost:9999/.well-known/jwks.json

配置两个OAuth2客户端,一个是Github的OAuth2的客户端,另一是自建OAuth2认证服务器的客户端。

4.运行与演示

对于“/bar”接口,同时支持Form认证和OAuth2客户端认证。

启动 “spring-security-url-separate-sample”

启动 “sauthorizationserver”

隐身模式打开浏览器,输入:http://localhost:8080/bar

两个示例来感受Spring Security认证的灵活性_第8张图片

此时我们有三种选择:

输入用户名/密码:admin/123456登录成功。

点击“Github”蓝色按钮,使用Github账号登录成功。

点击“自建OAuth2认证服务”蓝色按钮,使用自建OAuth2认证服务,输入用户名/密码;admin/admin,登录成功。

两个示例来感受Spring Security认证的灵活性_第9张图片

此示例演示了Spring Security的同组接口同时支持多种认证

五.总结

spring-security-url-separate-sample整体来看同时支持了Form、GitHub认证、自建OAuth2认证服务客户端认证和自建OAuth2认证服务资源服务认证四种认证方式,并且不冲突。每种认证方式都有各自的应用场景。需要注意多种认证组合时是可能产生冲突的。

Spring Security之所以可以实现如此灵活的认证配置,正是因为“按需装配”这一核心思想。首先无论Spring Security应用在Servlet还是响应式(Reactive)场景,都是一种AOP切面原理:

两个示例来感受Spring Security认证的灵活性_第10张图片

其次,一个web应用可以根据需求,需要使用哪种认证方式就装配哪种:

两个示例来感受Spring Security认证的灵活性_第11张图片

总之,Spring Security除了支持漏洞防御,Spring Security还针对认证和鉴权的需求做了非常强大且灵活的设计。

本文的相关源码上传到了Github,地址:https://github.com/andyzhaozhao/spring-security-url-separate

如果有任何问题和建议,可以右下角点赞后评论,我们会第一时间回复。

六.参考

更多干货都在《spring security实战》

两个示例来感受Spring Security认证的灵活性_第12张图片

你可能感兴趣的:(两个示例来感受Spring Security认证的灵活性)