SpringSecurity Oauth2实战一

SpringSecurity&Oauth2实战一


一、简单介绍

SpringSecurity是什么?

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

Oauth2

OAuth2.0是OAuth协议的延续版本,但不向前兼容OAuth 1.0(即完全废止了OAuth1.0)。 OAuth 2.0关注客户端开发者的简易性。要么通过组织在资源拥有者和HTTP服务商之间的被批准的交互动作代表用户,要么允许第三方应用代表用户获得访问的权限。同时为Web应用,桌面应用和手机,和起居室设备提供专门的认证流程。2012年10月,OAuth 2.0协议正式发布为RFC 6749

二、实战开始

1. OAuth2.0介绍

OAuth(Open Authorization)是一个关于授权(authorization)的开放网络标准,允许用户授权第三方应用
访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的
所有内容。OAuth在全世界得到广泛应用,目前的版本是2.0版。
OAuth协议:https://tools.ietf.org/html/rfc6749
协议特点:

  • 简单:不管是OAuth服务提供者还是应用开发者,都很易于理解与使用;
  • 安全:没有涉及到用户密钥等信息,更安全更灵活;
  • 开放:任何服务提供商都可以实现OAuth,任何软件开发商都可以使用OAuth;

1.1 应用场景

  • 原生app授权:app登录请求后台接口,为了安全认证,所有请求都带token信息,如果登录验证、请
    求后台数据。
  • 前后端分离单页面应用:前后端分离框架,前端请求后台数据,需要进行oauth2安全认证,比如使用
    vue、react后者h5开发的app
  • 第三方应用授权登录,比如QQ,微博,微信的授权登录。
    有一个"云冲印"的网站,可以将用户储存在Google的照片,冲印出来。用户为了使用该服务,必须让"云冲
    印"读取自己储存在Google上的照片。只有得到用户的授权,Google才会同意"云冲印"读取这些照片。那么,“云
    冲印"怎样获得用户的授权呢?
    传统方法是,用户将自己的Google用户名和密码,告诉"云冲印”,后者就可以读取用户的照片了。这样的做法有
    以下几个严重的缺点:
  • "云冲印"为了后续的服务,会保存用户的密码,这样很不安全。
  • Google不得不部署密码登录,而我们知道,单纯的密码登录并不安全。
  • "云冲印"拥有了获取用户储存在Google所有资料的权力,用户没法限制"云冲印"获得授权的范围和有
    效期。
  • 用户只有修改密码,才能收回赋予"云冲印"的权力。但是这样做,会使得其他所有获得用户授权的第
    三方应用程序全部失效。
  • 只要有一个第三方应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的数据泄漏。
    在这里插入图片描述
    生活中常见的oauth2场景,京东商城(https://www.jd.com/)接入微信开放平台,可以通过微信登录。
    在这里插入图片描述
    登录流程分析:
    在这里插入图片描述
    微信开发平台文档:
    https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html

1.2 基本概念

**(1)Third­party application:**第三方应用程序,又称"客户端"(client),即例子中的"云冲印"。
(2)HTTP service:HTTP服务提供商,简称"服务提供商",即例子中的Google。
(3)Resource Owner:资源所有者,又称"用户"(user)。
**(4)User Agent:**用户代理,比如浏览器。
**(5)Authorization server:**授权服务器,即服务提供商专门用来处理认证授权的服务器。
**(6)Resource server:**资源服务器,即服务提供商存放用户生成的资源的服务器。它与授权服务器,可以是
同一台服务器,也可以是不同的服务器。
OAuth的作用就是让"客户端"安全可控地获取"用户"的授权,与"服务提供商"进行交互。

1.3 优缺点

优点:

  • 更安全,客户端不接触用户密码,服务器端更易集中保护
  • 广泛传播并被持续采用
  • 短寿命和封装的token
  • 资源服务器和授权服务器解耦
  • 集中式授权,简化客户端
  • HTTP/JSON友好,易于请求和传递token
  • 考虑多种客户端架构场景
  • 客户可以具有不同的信任级别
    缺点:
  • 协议框架太宽泛,造成各种实现的兼容性和互操作性差
  • 不是一个认证协议,本身并不能告诉你任何用户信息。

2. OAuth2的设计思路

OAuth在"客户端"与"服务提供商"之间,设置了一个授权层(authorization layer)。“客户端"不能直接登录"服务
提供商”,只能登录授权层,以此将用户与客户端区分开来。"客户端"登录授权层所用的令牌(token),与用户
的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期,"客户端"登录授权层以后,"服务
提供商"根据令牌的权限范围和有效期,向"客户端"开放用户储存的资料。
OAuth 2.0的运行流程如下图,摘自RFC 6749:
在这里插入图片描述
(A)用户打开客户端以后,客户端要求用户给予授权。
(B)用户同意给予客户端授权。
(C)客户端使用上一步获得的授权,向授权服务器申请令牌。
(D)授权服务器对客户端进行认证以后,确认无误,同意发放令牌。
(E)客户端使用令牌,向资源服务器申请获取资源。
(F)资源服务器确认令牌无误,同意向客户端开放资源。
令牌(token)与密码(password)的作用是一样的,都可以进入系统,但是有三点差异。
(1)令牌是短期的,到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。
(2)令牌可以被数据所有者撤销,会立即失效。密码一般不允许被他人撤销。
(3)令牌有权限范围(scope)。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。
上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全。这就是 OAuth 2.0 的优
点。

2.1 客户端授权模式

客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0 对于如何颁发
令牌的细节,规定得非常详细。具体来说,一共分成四种授权类型(authorization grant),即四种颁发令牌的
方式,适用于不同的互联网场景。
授权码模式(authorization code)
密码模式(resource owner password credentials)
简化(隐式)模式(implicit)
客户端模式(client credentials)
不管哪一种授权方式,第三方应用申请令牌之前,都必须先到系统备案,说明自己的身份,然后会拿到两个身份
识别码:客户端 ID(client ID)和客户端密钥(client secret)。这是为了防止令牌被滥用,没有备案过的第三
方应用,是不会拿到令牌的。
在这里插入图片描述
在这里插入图片描述
它的步骤如下:

(A)用户访问客户端,后者将前者导向授权服务器。
(B)用户选择是否给予客户端授权。
(C)假设用户给予授权,授权服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权
码。
(D)客户端收到授权码,附上早先的"重定向URI",向授权服务器申请令牌。这一步是在客户端的后台的服务器上完成
的,对用户不可见。
(E)授权服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh
token)。

1. A网站提供一个链接,用户点击后就会跳转到 B 网站,授权用户数据给 A 网站使用。下面就是 A 网站跳转 B
网站的一个示意链接。

1 > https://b.com/oauth/authorize?
2 > response_type=code& #要求返回授权码(code)
3 > client_id=CLIENT_ID& #让 B 知道是谁在请求
4 > redirect_uri=CALLBACK_URL& #B 接受或拒绝请求后的跳转网址
5 > scope=read # 要求的授权范围(这里是只读)
6 >

客户端申请授权的URI,包含以下参数:

  • response_type:表示授权类型,必选项,此处的值固定为"code"
  • client_id:表示客户端的ID,必选项
  • redirect_uri:表示重定向URI,可选项
  • scope:表示申请的权限范围,可选项
  • state:表示客户端的当前状态,可以指定任意值,授权服务器会原封不动地返回这个值。
  1. 用户跳转后,B 网站会要求用户登录,然后询问是否同意给予 A 网站授权。用户表示同意,这时 B 网站就会
    跳回redirect_uri参数指定的网址。跳转时,会传回一个授权码,就像下面这样。
1 > https://a.com/callback?code=AUTHORIZATION_CODE #code参数就是授权码
2 >
  1. A 网站拿到授权码以后,就可以在后端,向 B 网站请求令牌。 用户不可见,服务端行为
1 > https://b.com/oauth/token?
2 > client_id=CLIENT_ID&
3 > client_secret=CLIENT_SECRET& # client_id和client_secret用来让 B 确认 A 的身份,client_secret参数是保密的,因
此只能在后端发请求
4 > grant_type=authorization_code& # 采用的授权方式是授权码
5 > code=AUTHORIZATION_CODE& # 上一步拿到的授权码
6 > redirect_uri=CALLBACK_URL # 令牌颁发后的回调网址
7 >
  1. B 网站收到请求以后,就会颁发令牌。具体做法是向redirect_uri指定的网址,发送一段 JSON 数据。
1 > {
2 > "access_token":"ACCESS_TOKEN", # 令牌
3 > "token_type":"bearer",
4 > "expires_in":2592000,
5 > "refresh_token":"REFRESH_TOKEN",
6 > "scope":"read",
7 > "uid":100101,
8 > "info":{...}
9 > }

简化(隐式)模式
有些 Web 应用是纯前端应用,没有后端。这时就不能用上面的方式了,必须将令牌储存在前端**。RFC 6749 就
规定了第二种方式,允许直接向前端颁发令牌,这种方式没有授权码这个中间步骤,所以称为(授权码)“**隐藏
式”(implicit)
简化模式不通过第三方应用程序的服务器,直接在浏览器中向授权服务器申请令牌,跳过了"授权码"这个步骤,
所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。
这种方式把令牌直接传给前端,是很不安全的。因此,只能用于一些安全要求不高的场景,并且令牌的有效期必
须非常短,通常就是会话期间(session)有效,浏览器关掉,令牌就失效了。
在这里插入图片描述
它的步骤如下
(A)客户端将用户导向授权服务器。
(B)用户决定是否给于客户端授权。
(C)假设用户给予授权,授权服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。
(D)浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。
(E)资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。
(F)浏览器执行上一步获得的脚本,提取出令牌。
(G)浏览器将令牌发给客户端。

  1. A 网站提供一个链接,要求用户跳转到 B 网站,授权用户数据给 A 网站使用。
1 > https://b.com/oauth/authorize?
2 > response_type=token& # response_type参数为token,表示要求直接返回令牌
3 > client_id=CLIENT_ID&
4 > redirect_uri=CALLBACK_URL&
5 > scope=read

6 >
2. 用户跳转到 B 网站,登录后同意给予 A 网站授权。这时,B 网站就会跳回redirect_uri参数指定的跳转网址,
并且把令牌作为 URL 参数,传给 A 网站。

1 > https://a.com/callback#token=ACCESS_TOKEN #token参数就是令牌,A 网站直接在前端拿到令牌。
2 >

密码模式
如果你高度信任某个应用,RFC 6749 也允许用户把用户名和密码,直接告诉该应用。该应用就使用你的密码,
申请令牌,这种方式称为"密码式"(password)。

在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任
的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而授权服务器只有在其他授权模式无法
执行的情况下,才能考虑使用这种模式。
适用场景:自家公司搭建的授权服务器
在这里插入图片描述
它的步骤如下:
(A)用户向客户端提供用户名和密码。
(B)客户端将用户名和密码发给授权服务器,向后者请求令牌。
(C)授权服务器确认无误后,向客户端提供访问令牌。
1. A 网站要求用户提供 B 网站的用户名和密码,拿到以后,A 就直接向 B 请求令牌。整个过程中,客户端不得保存用户的密
码。

 https://oauth.b.com/token?
2 > grant_type=password& # 授权方式是"密码式"
3 > username=USERNAME&
4 > password=PASSWORD&
5 > client_id=CLIENT_ID

2. B 网站验证身份通过后,直接给出令牌。注意,这时不需要跳转,而是把令牌放在 JSON 数据里面,作为
HTTP 回应,A 因此拿到令牌。

在这里插入图片描述

2.2 令牌的使用

A 网站拿到令牌以后,就可以向 B 网站的 API 请求数据了。
此时,每个发到 API 的请求,都必须带有令牌。具体做法是在请求的头信息,加上一个Authorization字段,令
牌就放在这个字段里面。

curl ‐H "Authorization: Bearer ACCESS_TOKEN" \
2 > "https://api.b.com"

也可以通过添加请求参数access_token请求数据。
在这里插入图片描述

2.3 更新令牌

令牌的有效期到了,如果让用户重新走一遍上面的流程,再申请一个新的令牌,很可能体验不好,而且也没有必
要。OAuth 2.0 允许用户自动更新令牌。
在这里插入图片描述
具体方法是,B 网站颁发令牌的时候,一次性颁发两个令牌,一个用于获取数据,另一个用于获取新的令牌

(refresh token 字段)。令牌到期前,用户使用 refresh token 发一个请求,去更新令牌。
> https://b.com/oauth/token?
2 > grant_type=refresh_token& # grant_type参数为refresh_token表示要求更新令牌
3 > client_id=CLIENT_ID&
4 > client_secret=CLIENT_SECRET&
5 > refresh_token=REFRESH_TOKEN # 用于更新令牌的令牌
6 >

在这里插入图片描述

3. Spring Security OAuth2快速开始

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架
Spring Security 主要实现了Authentication(认证,解决who are you? ) 和 Access Control(访问控制,也
就是what are you allowed to do?,也称为Authorization)。Spring Security在架构上将认证与授权分离,并
提供了扩展点。
认证(Authentication) :用户认证就是判断一个用户的身份是否合法的过程,用户去访问系统资源时系统要求验证用户
的身份信息,身份合法方可继续访问,不合法则拒绝访问。常见的用户身份认证方式有:用户名密码登录,二维码登录,
手机短信登录,指纹认证等方式。
授权(Authorization): 授权是用户认证通过根据用户的权限来控制用户访问资源的过程,拥有资源的访问权限则正常访
问,没有权限则拒绝访问。
将OAuth2和Spring Security集成,就可以得到一套完整的安全解决方案。我们可以通过Spring Security
OAuth2构建一个授权服务器来验证用户身份以提供access_token,并使用这个access_token来从资源服务器
请求数据。

3.1 授权服务器

在这里插入图片描述

  • Authorize Endpoint :授权端点,进行授权
  • Token Endpoint :令牌端点,经过授权拿到对应的Token
  • Introspection Endpoint :校验端点,校验Token的合法性
  • Revocation Endpoint :撤销端点,撤销授权

3.2 整体架构

在这里插入图片描述
流程:

  1. 用户访问,此时没有Token。Oauth2RestTemplate会报错,这个报错信息会被Oauth2ClientContextFilter
    捕获并重定向到授权服务器。
  2. 授权服务器通过Authorization Endpoint进行授权,并通过AuthorizationServerTokenServices生成授权码
    并返回给客户端。
  3. 客户端拿到授权码去授权服务器通过Token Endpoint调用AuthorizationServerTokenServices生成Token
    并返回给客户端
  4. 客户端拿到Token去资源服务器访问资源,一般会通过Oauth2AuthenticationManager调用
    ResourceServerTokenServices进行校验。校验通过可以获取资源。

3.3 授权码模式

引入依赖

       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>

或者 引入spring cloud oauth2依赖

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
         <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR8</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

配置 spring security

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    
    
    @Autowired
    private UserService userService;
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 获取用户信息
        auth.userDetailsService(userService);
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin().permitAll()
                .and().authorizeRequests()
                .antMatchers("/oauth/**").permitAll()
                .antMatchers("/order/**").permitAll()
                .anyRequest().authenticated()
                .and().logout().permitAll()
                .and().csrf().disable();

    }
    
    

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

配置授权服务器

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

//    @Autowired
//    private TokenStore tokenStore;


//    @Override
//    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//        endpoints.tokenStore(tokenStore)  //指定token存储到redis
//                .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST); //支持GET,POST请求
//    }
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //允许表单认证
        security.allowFormAuthenticationForClients();

    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //授权码模式
        //http://localhost:8080/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=all
        // 简化模式
        //http://localhost:8080/oauth/authorize?response_type=token&client_id=client&redirect_uri=http://www.baidu.com&scope=all

        clients.inMemory()
                //配置client_id
                .withClient("client")
                //配置client-secret
                .secret(passwordEncoder.encode("123123"))
                //配置访问token的有效期
                .accessTokenValiditySeconds(3600)
                //配置刷新token的有效期
                .refreshTokenValiditySeconds(864000)
                //配置redirect_uri,用于授权成功后跳转
                .redirectUris("http://www.baidu.com")
                //配置申请的权限范围
                .scopes("all")
                //配置grant_type,表示授权类型
                .authorizedGrantTypes("authorization_code","implicit");


    }
}

配置资源服务器


/**
 * @author wcg
 * 资源服务器
 */
@Configuration
@EnableResourceServer
public class ResourceServiceConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
                .anyRequest().authenticated()
                .and().requestMatchers().antMatchers("/user/**");

    }


}

访问http://localhost:8080/user/getCurrentUser
提示需要授权
在这里插入图片描述
授权码模式:
http://localhost:8080/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=all
在这里插入图片描述
点击授权之后->https://www.baidu.com/?code=sdvffS

根据code 访问http://localhost:8080/oauth/token获取token
参数
grant_type:authorization_code
code:FXx4at
redirect_uri:http://www.baidu.com
client_id:client
client_secret:123123
在这里插入图片描述
根据Access_token访问
http://localhost:8080/user/getCurrentUser?access_token=144c0b2d-ea58-4c17-8c82-3cf45b1c9b51
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Security OAuth2是一个基于Spring Security的扩展,用于实现OAuth2协议中的认证和授权功能。它提供了多种认证模式,其中包括client模式。在client模式下,客户端直接通过自己的凭证向授权服务器进行认证,获取访问令牌后可以直接访问受保护的资源。 实现单点登录功能可以使用Spring Security OAuth2来实现。单点登录是指用户只需要登录一次,就可以访问多个相互信任的应用系统。在Spring Security OAuth2中,可以通过配置多个客户端来实现单点登录。每个客户端都有自己的clientId和clientSecret,当用户登录成功后,会生成一个访问令牌,并将该令牌保存在认证服务器中。其他应用系统可以通过验证访问令牌的方式来实现单点登录。 具体的实现步骤可以参考以下链接: - \[Spring Cloud SecurityOauth2实现单点登录\](https://www.macrozheng.com/cloud/oauth2_sso.html#oauth2-client%E6%B7%BB%E5%8A%A0%E6%9D%83%E9%99%90%E6%A0%A1%E9%AA%8C) \[1\] - \[简述 Spring Security OAuth2的认证client模式\](https://www.kancloud.cn/zhangchio/springboot/663138) \[2\] - \[spring-security-oauth2是对OAuth2协议中授权类型的具体实现,也是我们实现单点登录功能实际用到的代码\](https://blog.csdn.net/pxg943055021/article/details/124752669) \[3\] 通过参考以上链接,你可以了解如何使用Spring Security OAuth2来实现单点登录功能。 #### 引用[.reference_title] - *1* *2* *3* [Spring Cloud SecurityOauth2 单点登录](https://blog.csdn.net/qq_19636353/article/details/127025830)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值