Oauth2.0 认证

2023-12-14 04:48:00

目录

前言

1.介绍

2.Oauth2.0过程详解?

3.Oauth 整合到 Spring Boot 实践?

4.方法及配置详解:

总结


前言

Oauth2.0 是非常流行的网络授权表准,已经广泛应用在全球范围内,比较大的公司,如腾讯等都有大量的应用场景。


1.介绍

Oauth2.0 全称是 Open Authorization,是一种开放授权协议。目前使用的版本是 2.0 版本,也就是?Oauth2.0,它主要用于授权认证环节。

从官网文档可知道 Oauth 具有如下特点:

  1. 需要第三方进行授权,会存储用户登录授权凭据。
  2. 服务器必须支持密码认证。
  3. 有限范围内获取所有者(用户)的部分数据。?

简而言之,就是用于第三方在用户授权的前提下获取用户的相关信息。而 Oauth2.0 的授权模式比较多,常用的有如下四种:

  1. 授权码模式:最常规的模式,适用于有服务器端的第三方应用(如网页应用)。在这种模式下,用户代理(如浏览器)首先被重定向到授权服务器进行身份验证,授权服务器通过用户代理回调客户端,并提供一个授权码。客户端随后会使用这个授权码向授权服务器后端请求访问令牌(access token),支持刷新的 token。
  2. Clinet 模式:适用于客户端应用直接访问它们自己的资源服务器,没有用户直接参与的场景。在这个模式中,客户端应用凭借自己的凭据(客户端ID及客户端密钥)向授权服务器请求访问令牌。其他应用或者程序通过 api 进行调用,获取对应的 token。
  3. 简化模式:用于没有服务器端能力的客户端应用,如 JavaScript 应用。在简化模式中,客户端通过用户代理直接从授权服务器获取访问令牌,不通过中间的授权码交换步骤。由于这种模式暴露了访问令牌,因此不如授权码模式安全。
  4. 密码模式?:如果用户对第三方应用高度信任(例如,设备操作系统或具备原生应用的第三方服务),则可以直接将用户名和密码提供给应用,应用使用这些凭据直接从授权服务器获取访问令牌。由于此流程涉及明文传递用户凭据,因此安全性较低。

总结:从中其实可以看出,最关键的部分就是这三方相互确认的过程。授权码模式就可以看成三方之间都不信任,所以需要不断地相互确认;而简化模式则可以看作授权服务器对第三方应用比较信任,表示直接就给了你我的令牌,你并不会拿我的令牌去做坏事,也就是说它们之间比较互信;密码模式则是资源拥有者对第三方应用比较信任,可以将自己的信息放心的放给第三方;客户端模式则可以看成三方都比较信任,不需要过多的验证,请求就给令牌就行了,比较适合我们内部的应用场景。

梳理 Oauth2.0 中有一个主线,就是三方参与者之间的信任程度

2.Oauth2.0过程详解?

前面我们对四个常见的模式进行了详细的了解,为了更加方便理解,对基本的过程再次进行详细解释。Oauth 的基本认正过程如图,分为三个角色:用户、第三方、以及认证服务器。

首先,用户去访问第三方应用,第三方应用引导用户进行授权,跳转到授权页面。用户进行授权后将数据传递给认证服务器,认证服务器返回 code 给第三方应用,第三方应用发起新的请求来获取访问授权令牌(access token),最后用户获取到授权结果。?

3.Oauth 整合到 Spring Boot 实践?

客户端凭证是最常用的认证方式之一。例如,微信授权就是这种方式,通过携带 access token 来获取用户资源。

新建Spring Boot 项目,实现方案是使用 SpringSecurity 和 Oauth2.0 模块,pom.xml 添加如下依赖:

        <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.5.RELEASE</version>
        </dependency>

为了实现 Oauth 认证,需要对两方面进行配置,一是认证服务配置,包含 token?定义,用户客户端的信息以及授权服务和令牌服务。二是需要对资源服务进行配置,如资源访问权限设置,哪些需要 token 验证。

首先,编写认证服务配置,定义授权以及令牌服务。

package org.example.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

/**
 * 认证服务器类
 *
 * @author freejava
 */
@Configuration
//此注解不像其他的 Enable注解一样内部有 Component 注解
@EnableAuthorizationServer
public class MySuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private AuthorizationCodeServices authorizationCodeServices;

    //令牌访问安全策略
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //用于表单方式提交 client_id,client_secret
        security.tokenKeyAccess("permitAll()")
                        .checkTokenAccess("permitAll()").
                allowFormAuthenticationForClients();
    }

    @Bean
    public PasswordEncoder noOpPasswordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    /**
     *  配置客户端信息 哪些客户端可以发送请求
     * @param clients 定义客户端信息的配置们,可以初始化客户端信息。
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
       clients.inMemory()
               //client_id
               .withClient("myClientId")
               // client_secret
               .secret(NoOpPasswordEncoder.getInstance().encode("123456"))
               //授权方式
               .authorizedGrantTypes("authorization_code", "password","client_credentials","implicit","refresh_token")
               .resourceIds("res1")//可以访问哪些资源
               //授权范围
               .scopes("all")//write
               .autoApprove(false)//false跳转到授权页面
               //加上验证回调地址
               .redirectUris("http://www.baidu.com");
    }

    @Autowired
    private ClientDetailsService clientDetailsService;


    // 令牌服务
    @Bean
    public AuthorizationServerTokenServices tokenService(){
        DefaultTokenServices services = new DefaultTokenServices();
        services.setClientDetailsService(clientDetailsService);
        services.setSupportRefreshToken(true);
        services.setTokenStore(tokenStore());
        services.setAccessTokenValiditySeconds(7200);//2小时
        services.setRefreshTokenValiditySeconds(259200);//刷新令牌默认有效期 3 天
        return services;
    }
    //设置授权码模式如何存储
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new InMemoryAuthorizationCodeServices();
    }
    private String SIGNING_KEY = "uaa123";
    //内存方式
    @Bean
    public TokenStore tokenStore(){
        return  new InMemoryTokenStore();//new JwtTokenStore(accessTokenConverter());
    }
   /* @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证
        return converter;
    }*/

    /**
     * 定义授权和令牌服务
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //super.configure(endpoints);
        endpoints.authenticationManager(authenticationManager)
                .authorizationCodeServices(authorizationCodeServices)//授权码服务
                .tokenServices(tokenService())//令牌管理服务
                .allowedTokenEndpointRequestMethods(HttpMethod.POST,HttpMethod.GET);//允许POST提交

    }
}

资源配置编写,先编写一个 Controller 文件,代码如下:

package org.example.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ResController {
    @GetMapping("/res/{id}")
    public String testOauth(@PathVariable String id) {
        return "Get the resource " + id;
    }
}

然后编写该资源的访问权限配置,代码如下:

package org.example.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;

/**
 * 用于拦截请求的配置类
 */
@Configuration
@EnableResourceServer
public class MyResourceServerConfigurer extends ResourceServerConfigurerAdapter {
    @Autowired
    private TokenStore tokenStore;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        //这里重点注意
        resources.tokenStore(tokenStore).resourceId("res1");
    }


    /**
     * 用于拦截 http 请求
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {

        http.csrf().disable().
                authorizeRequests()
                .antMatchers("/oauth/**").permitAll() // 允许所有人访问 /oauth/* 下面的内容。
                .anyRequest().authenticated(); // 其他所有请求都需要认证。
        /*http.authorizeRequests().anyRequest()
                .authenticated();
                        //("/res/**").authenticated();*/
    }
}

为授权码模式编写一个webconfig:

package org.example.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author Administrator
 * @version 1.0
 **/
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    //认证管理器
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    //安全拦截机制(最重要)
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/orther/**").hasAnyAuthority("p1")
                .antMatchers("/login*").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin();

    }
}

通过 Postman 进行测试,访问 localhost:8080/res/1 会返回一个 unauthorized 的错误返回,这里需要传递 access token,所以需要先请求获取 access token 的接口 /oauth/token,之后再用该token进行请求即可。?

4.方法及配置详解:

ClientDetailsServiceConfigurer?详解:?

ClientDetailsServiceConfigurer?是用于配置客户端详细信息服务的类。客户端详细信息服务定义了客户端(应用程序)的信息,这包括客户端ID、客户端密钥、授权模式、令牌有效期以及重定向的URI等信息。客户端详细信息可以通过硬编码、数据库等方式来存储和管理。

以下是?ClientDetailsServiceConfigurer?的一些常用方法:

  1. inMemory()
    在内存中配置客户端存储,主要用于测试或简单应用程序,不适合生产使用。

  2. jdbc(DataSource dataSource)
    使用 JDBC 连接数据库,并在数据库中维护客户端信息。

  3. withClient(String clientId)
    创建新的客户端,并设置其客户端id。

  4. secret(String secret)
    设置客户端的密钥。对于使用Spring Security 5的应用程序,需要注意密码存储格式,通常是?{bcrypt}{noop}?等。

  5. redirectUris(String… redirectUris)
    设置客户端的重定向URI,这些URI在OAuth2授权码模式下使用。

  6. authorizedGrantTypes(String… authorizedGrantTypes)
    设置客户端允许的授权模式,如?authorization_code,?password,?refresh_token,?client_credentials

  7. authorities(String… authorities)
    为客户端设置Spring Security的权限。

  8. scopes(String… scopes)
    设置客户端的作用域(scope),限定客户端的访问范围。

  9. accessTokenValiditySeconds(int accessTokenValiditySeconds)
    设置访问令牌的有效时间(以秒计)。

  10. refreshTokenValiditySeconds(int refreshTokenValiditySeconds)
    设置刷新令牌的有效时间(以秒计)。

  11. additionalInformation(Map<String, ?> additionalInformation)
    设置一个包含其他客户端详细信息的Map。

  12. autoApprove(boolean autoApprove)
    如果设置为true,则不需要用户在授权阶段进行手动批准。

  13. autoApprove(String… scopes)
    指定特定范围的自动批准。

  14. resourceIds(String… resourceIds)
    限定客户端可以访问的资源服务器的ID。?

AuthorizationServerEndpointsConfigurer详解:

默认得到访问路径:

  • /oauth/authorize: 授权端点
  • /oauth/token : 令牌端点。
  • /loauth/confirm_access : 用户确认授权提交端点。
  • /loauth/error: 授权服务错误信息端点。
  • /oauth/check_token : 用于资源服务访问的令牌解析端点
  • /oauth/token_key : 提供公有密匙的端点,如果你使用JWT令牌的话.?

AuthorizationServerEndpointsConfigurer是Spring Security OAuth2中用于定义授权服务器端点配置的类,它允许你定义与认证相关的各种参数和实现。也就是配置令牌的访问端点(发放令牌的地址)和令牌服务(如何发放),下面是一些?AuthorizationServerEndpointsConfigurer?的主要配置方法,以及它们的用途:

  1. authenticationManager(AuthenticationManager authenticationManager)
    设置认证管理器。在password授权模式下,需要配置此项来验证用户的用户名和密码。

  2. userDetailsService(UserDetailsService userDetailsService)
    设置用户细节服务。如果你设置了一个自定义的用户细节服务,可以通过这个方法来配置。

  3. authorizationCodeServices(AuthorizationCodeServices authorizationCodeServices)
    设置授权码服务。用于authorization_code授权类型。这能够改变默认的授权码存储方式(通常是内存)。

  4. tokenStore(TokenStore tokenStore)
    设置令牌存储方式。可以使用各种不同的存储方式,如内存、JDBC或JWT tokens。

  5. accessTokenConverter(AccessTokenConverter accessTokenConverter)
    设置访问令牌转换器。例如,使用JWT token时,可以配置一个JwtAccessTokenConverter

  6. tokenEnhancer(TokenEnhancer tokenEnhancer)
    设置令牌增强器。可以用来扩展JWT token内容。

  7. approvalStore(ApprovalStore approvalStore)
    设置批准存储。这跟token的保存方式类似,定义了用户批准记录的存储方式。

  8. reuseRefreshTokens(boolean reuseRefreshTokens)
    设置是否复用refresh tokens。默认为true,即当新的access token被创建后,原始的refresh token将会被复用。

  9. refreshTokenGranter(TokenGranter refreshTokenGranter)
    设置自定义的refresh token授权者。

  10. grantTypes(TokenGranter tokenGranter)
    设置自定义授权类型,可以添加或覆盖默认的授权类型来扩展功能。

  11. tokenValidator(OAuth2TokenValidator<Jwt> tokenValidator)
    在使用JWT时设置令牌验证器,用以验证JWT的有效性。

  12. tokenServices(DefaultTokenServices tokenServices)
    设置token服务的实例。这个服务负责创建和管理OAuth2 tokens。

  13. exceptionTranslator(WebResponseExceptionTranslator<OAuth2Exception> exceptionTranslator)
    设置异常翻译器。用来定义认证异常的响应格式。

  14. requestFactory(OAuth2RequestFactory requestFactory)
    设置OAuth2请求工厂。这允许你插入自定义逻辑来解析传入的请求。?

AuthorizationServerSecurityConfigurer :

用来配置令牌端点的安全约束,比如哪些人可以访问。

ResourceServerSecurityConfigurer详解

ResourceServerSecurityConfigurer?是 Spring Security OAuth2 中用于配置资源服务器的安全细节的一个类。它允许你设置一些关于资源服务器的关键配置,比如 tokenStore、resourceId 和 tokenServices 等。下面是一些?ResourceServerSecurityConfigurer?的常用方法及其简要说明:

  1. tokenStore(TokenStore tokenStore)
    设置用于读取令牌信息的?TokenStore。这个?TokenStore?被资源服务器用来解码访问令牌。常见的实现是?JdbcTokenStoreJwtTokenStore?和?InMemoryTokenStore

  2. resourceId(String resourceId)
    设置此资源服务器的资源ID。这通常用于客户端请求的 Scope 限制,以及在多个资源服务器存在时加以区分。

  3. tokenServices(TokenServices tokenServices)
    设置?ResourceServerTokenServices?实例,这是用来解码访问令牌的另一种方式。如果你的令牌是 JWT,则可以提供?JwtTokenServices

  4. tokenExtractor(TokenExtractor tokenExtractor)
    设置用于提取访问令牌的?TokenExtractor。默认情况下,Spring OAuth2 使用?BearerTokenExtractor?来处理来自请求头或请求参数的 Bearer 令牌。

  5. authenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint)
    定制用于处理认证错误的?AuthenticationEntryPoint。通常情况下,这用于处理资源服务器返回给客户端的401响应。

  6. accessDeniedHandler(AccessDeniedHandler accessDeniedHandler)
    设置自定义?AccessDeniedHandler?以处理接收到访问被拒绝时的情况。这通常用来自定义服务器对403 Forbidden响应的处理。

  7. stateless(Boolean stateless)
    设置此资源服务器是否只关心无状态请求应答。这确定了资源服务器是否会在请求间创建?HttpSession

  8. authenticationManager(AuthenticationManager authenticationManager)
    设置?AuthenticationManager。如果需要,可以用来登陆用户或验证用户的访问令牌。

  9. eventPublisher(ApplicationEventPublisher eventPublisher)
    设置?ApplicationEventPublisher?以发布认证事件。这可以用来记录认证成功或者失败等事件。


总结

OAuth 2.0 是一个用于授权的开放标准,由 IETF OAuth 工作组于 2012 年发布。它允许用户授权第三方应用访问其在某一服务上的信息,而无需将用户名和密码提供给第三方应用,大大促进了当前互联网间的信息共享。

文章来源:https://blog.csdn.net/m0_62943934/article/details/134885858
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。