Spring Cloud OAuth2 认证服务器

1、Spring Cloud OAuth2介绍

        Spring Cloud OAuth2 是 Spring Cloud 体系对OAuth2协议的实现,可以用来做多个微服务的统一认证(验证身份合法性)授权(验证权限)。通过向OAuth2服务(统一认证授权服务)发送某个类型的grant_type进行集中认证和授权,从而获得access_token(访问令牌),而这个token是受其他微服务信任的。

        注意:使用OAuth2解决问题的本质是,引入了一个认证授权层,认证授权层连接了资源的拥有者,在授权层里面,资源的拥有者可以给第三方应用授权去访问我们的某些受保护资源。

2、Spring Cloud OAuth2构建微服务统一认证服务思路

        注意:在我们统一认证的场景中,Resource Server其实就是我们的各种受保护的微服务,微服务中的各种API访问接口就是资源,发起http请求的浏览器就是Client客户端(对应为第三方应用) 

3、搭建认证服务器(Authorization Server)

  认证服务器(Authorization Server),负责颁发token

(1)新建项目lagou-cloud-oauth-server-9999

(2)pom.xml

 <dependencies>
        <!--导入Eureka Client依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>


        <!--导入spring cloud oauth2依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.security.oauth.boot</groupId>
                    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>2.1.11.RELEASE</version>
        </dependency>
        <!--引入security对oauth2的支持-->
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--操作数据库需要事务控制-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
        </dependency>

(3)application.yml(构建认证服务器,配置文件无特别之处)

server:
  port: 9999
Spring:
  application:
    name: lagou-cloud-oauth-server
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/oauth2?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
    username: root
    password: 123456
    druid:
      initialSize: 10
      minIdle: 10
      maxActive: 30
      maxWait: 50000
eureka:
  client:
    serviceUrl: # eureka server的路径
      defaultZone: http://lagoucloudeurekaservera:8761/eureka/,http://lagoucloudeurekaserverb:8762/eureka/ #把 eureka 集群中的所有 url 都填写了进来,也可以只写一台,因为各个 eureka server 可以同步注册表
  instance:
    #使用ip注册,否则会使用主机名注册了(此处考虑到对老版本的兼容,新版本经过实验都是ip)
    prefer-ip-address: true
    #自定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@

(4)启动类

package com.lagou.edu;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class OauthServerApplication9999 {

    public static void main(String[] args) {
        SpringApplication.run(OauthServerApplication9999.class,args);
    }
}

(5)认证服务器配置类

package com.lagou.edu.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.jwt.crypto.sign.MacSigner;
import org.springframework.security.jwt.crypto.sign.SignatureVerifier;
import org.springframework.security.jwt.crypto.sign.Signer;
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.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;


/**
 * 当前类为Oauth2 server的配置类(需要继承特定的父类 AuthorizationServerConfigurerAdapter)
 */
@Configuration
@EnableAuthorizationServer  // 开启认证服务器功能
public class OauthServerConfiger extends AuthorizationServerConfigurerAdapter {


    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * 认证服务器最终是以api接口的方式对外提供服务(校验合法性并生成令牌、校验令牌等)
     * 那么,以api接口方式对外的话,就涉及到接口的访问权限,我们需要在这里进行必要的配置
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        super.configure(security);
        // 相当于打开endpoints 访问接口的开关,这样的话后期我们能够访问该接口
        security
                // 允许客户端表单认证
                .allowFormAuthenticationForClients()
                // 开启端口/oauth/token_key的访问权限(允许)
                .tokenKeyAccess("permitAll()")
                // 开启端口/oauth/check_token的访问权限(允许)
                .checkTokenAccess("permitAll()");
    }

    /**
     * 客户端详情配置,
     *  比如client_id,secret
     *  当前这个服务就如同QQ平台,拉勾网作为客户端需要qq平台进行登录授权认证等,提前需要到QQ平台注册,QQ平台会给拉勾网
     *  颁发client_id等必要参数,表明客户端是谁
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        super.configure(clients);

        // 从内存中加载客户端详情

        clients.inMemory()// 客户端信息存储在什么地方,可以在内存中,可以在数据库里
                .withClient("client_lagou")  // 添加一个client配置,指定其client_id
                .secret("abcxyz")                   // 指定客户端的密码/安全码
                .resourceIds("autodeliver")         // 指定客户端所能访问资源id清单,此处的资源id是需要在具体的资源服务器上也配置一样
                // 认证类型/令牌颁发模式,可以配置多个在这里,但是不一定都用,具体使用哪种方式颁发token,需要客户端调用的时候传递参数指定
                .authorizedGrantTypes("password","refresh_token")
                // 客户端的权限范围,此处配置为all全部即可
                .scopes("all");

    }

    /**
     * 认证服务器是玩转token的,那么这里配置token令牌管理相关(token此时就是一个字符串,当下的token需要在服务器端存储,
     * 那么存储在哪里呢?都是在这里配置)
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        super.configure(endpoints);
        endpoints
                .tokenStore(tokenStore())  // 指定token的存储方法
                .tokenServices(authorizationServerTokenServices())   // token服务的一个描述,可以认为是token生成细节的描述,比如有效时间多少等
                .authenticationManager(authenticationManager) // 指定认证管理器,随后注入一个到当前类使用即可
                .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST);
    }


    /*
        该方法用于创建tokenStore对象(令牌存储对象)
        token以什么形式存储
     */
    public TokenStore tokenStore(){
        return new InMemoryTokenStore();
       
    }


    /**
     * 该方法用户获取一个token服务对象(该对象描述了token有效期等信息)
     */
    public AuthorizationServerTokenServices authorizationServerTokenServices() {
        // 使用默认实现
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setSupportRefreshToken(true); // 是否开启令牌刷新
        defaultTokenServices.setTokenStore(tokenStore());

        // 针对jwt令牌的添加
        defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter());

        // 设置令牌有效时间(一般设置为2个小时)
        defaultTokenServices.setAccessTokenValiditySeconds(20); // access_token就是我们请求资源需要携带的令牌
        // 设置刷新令牌的有效时间
        defaultTokenServices.setRefreshTokenValiditySeconds(259200); // 3天

        return defaultTokenServices;
    }
}

(6)关于三个configure方法

  • configure(ClientDetailsServiceConfigurer clients):用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息
  • configure(AuthorizationServerEndpointsConfigurer endpoints):用来配置令牌(token)的访问端点和令牌服务(token services)
  • configure(AuthorizationServerSecurityConfigurer oauthServer):用来配置令牌端点的安全约束.

(7)关于 TokenStore

  • InMemoryTokenStore:默认采用,它可以完美的工作在单服务器上(即访问并发量 压力不大的情况下,并且它在失败的时候不会进行备份),大多数的项目都可以使用这个版本的实现来进行尝试,你可以在开发的时候使用它来进行管理,因为不会被保存到磁盘中,所以更易于调试。
  • JdbcTokenStore:这是⼀个基于JDBC的实现版本,令牌会被保存进关系型数据库。使用这个版本的实现时, 你可以在不同的服务器之间共享令牌信息,使用这个版本的时候请注意"spring-jdbc"这个依赖加入到你的 classpath当中。
  • JwtTokenStore:这个版本的全称是 JSON Web Token(JWT),它可以把令牌相关的数据进行编码(因此对于后端服务来说,它不需要进行存储,这将是一个重大优势),缺点就是这个令牌占用的空间会比较大,如果你加⼊了比较多用户凭证信息,JwtTokenStore 不会保存任何数据。

(7)认证服务器安全配置类

package com.lagou.edu.config;

import com.lagou.edu.service.JdbcUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.quartz.QuartzProperties;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.ArrayList;


/**
 * 该配置类,主要处理用户名和密码的校验等事宜
 */
@Configuration
public class SecurityConfiger extends WebSecurityConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;


    /**
     * 注册一个认证管理器对象到容器
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


    /**
     * 密码编码对象(密码不进行加密处理)
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    /**
     * 处理用户名和密码验证事宜
     * 1)客户端传递username和password参数到认证服务器
     * 2)一般来说,username和password会存储在数据库中的用户表中
     * 3)根据用户表中数据,验证当前传递过来的用户信息的合法性
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 在这个方法中就可以去关联数据库了,当前我们先把用户信息配置在内存中
        // 实例化一个用户对象(相当于数据表中的一条用户记录)
        UserDetails user = new User("admin","123456",new ArrayList<>());
        auth.inMemoryAuthentication()
                .withUser(user).passwordEncoder(passwordEncoder);
    }
}

(8)测试

获取token:

http://localhost:9999/oauth/token?client_secret=abcxyz&grant_type=password&username=admin&password=123456&client_id=client_lagou
  • endpoint:/oauth/token
  • 获取token携带的参数
    • client_id:客户端id
    • client_secret:客户单密码
    • grant_type:指定使用哪种颁发类型,password
    • username:用户名
    • password:密码

校验token:

http://localhost:9999/oauth/check_token?token=a9979518-838c-49ff-b14a-ebdb7fde7d08

刷新token:

http://localhost:9999/oauth/token?grant_type=refresh_token&client_id=client_lagou&client_secret=abcxyz&refresh_token=8b640340-30a3-4307-93d4-ed60cc54fbc8 

4、资源服务器(希望访问被认证的微服务)Resource Server配置

        注:这里只是介绍了资源服务器如何嵌入认证服务的代码,当你想要某些资源服务受保护时,直接就在自己的项目中,嵌入这些代码即可。 

  • 导入认证相关依赖
     <!--导入spring cloud oauth2依赖-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-oauth2</artifactId>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework.security.oauth.boot</groupId>
                        <artifactId>spring-security-oauth2-autoconfigure</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.springframework.security.oauth.boot</groupId>
                <artifactId>spring-security-oauth2-autoconfigure</artifactId>
                <version>2.1.11.RELEASE</version>
            </dependency>
            <!--引入security对oauth2的支持-->
            <dependency>
                <groupId>org.springframework.security.oauth</groupId>
                <artifactId>spring-security-oauth2</artifactId>
                <version>2.3.4.RELEASE</version>
            </dependency>
        </dependencies>
  • 资源服务配置类
    package com.lagou.edu.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.jwt.crypto.sign.MacSigner;
    import org.springframework.security.jwt.crypto.sign.RsaVerifier;
    import org.springframework.security.jwt.crypto.sign.SignatureVerifier;
    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.RemoteTokenServices;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
    import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
    
    @Configuration
    @EnableResourceServer  // 开启资源服务器功能
    @EnableWebSecurity  // 开启web访问安全
    public class ResourceServerConfiger extends ResourceServerConfigurerAdapter {
    
        /**
         * 该方法用于定义资源服务器向远程认证服务器发起请求,进行token校验等事宜
         * @param resources
         * @throws Exception
         */
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    
            // 设置当前资源服务的资源id
            resources.resourceId("autodeliver");
            // 定义token服务对象(token校验就应该靠token服务对象)
            RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
            // 校验端点/接口设置
            remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:9999/oauth/check_token");
            // 携带客户端id和客户端安全码
            remoteTokenServices.setClientId("client_lagou");
            remoteTokenServices.setClientSecret("abcxyz");
    
            // 别忘了这一步
            resources.tokenServices(remoteTokenServices);
    
        }
    
    
        /**
         * 场景:一个服务中可能有很多资源(API接口)
         *    某一些API接口,需要先认证,才能访问
         *    某一些API接口,压根就不需要认证,本来就是对外开放的接口
         *    我们就需要对不同特点的接口区分对待(在当前configure方法中完成),设置是否需要经过认证
         *
         * @param http
         * @throws Exception
         */
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http    // 设置session的创建策略(根据需要创建即可)
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                    .and()
                    .authorizeRequests()
                    .antMatchers("/autodeliver/**").authenticated() // autodeliver为前缀的请求需要认证
                    .antMatchers("/demo/**").authenticated()  // demo为前缀的请求需要认证
                    .anyRequest().permitAll();  //  其他请求不认证
        }
    
    }
    

        注意:这个时候我们启动服务,浏览器输入 /autodeliver/** 此路径下进行访问,因为我们在上面配置类中设置了此类必须要认证才能够进行访问(携带token),否则就会提示未认证。而我们这个时候需要在访问路径后面拼上 access_token:具体内容,这样才能够访问我们必须要认证后才能够访问的服务,而没有设置必须认证的服务,则不必携带token直接就能就行服务调用。

        思考:当我们第一次登陆之后,认证服务器颁发token并将其存储在认证服务器中,后期我们访问资源服务器时会携带token,资源服务器会请求认证服务器验证token有效性,如果资源服务器有很多,那么认证服务器压力会很大....... 

        另外,资源服务器向认证服务器check_token,获取的也是用户信息UserInfo,能否把用户信息存储到令牌中,让客户端⼀直持有这个令牌,令牌的验证也在资源服务器进行,这样避免和认证服务器频繁的交互......我们可以考虑使用 JWT 进行改造,使用JWT机制之后资源服务器不需要访问认证服务器......

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Spring Cloud OAuth2是一个基于Spring CloudOAuth2认证和授权框架。它提供了一组工具和库,用于构建安全的分布式系统。Spring Cloud OAuth2支持多种OAuth2授权流程,包括授权码模式、密码模式、客户端模式和简化模式。它还提供了一些可扩展的组件,如令牌存储、用户信息服务和授权服务器。使用Spring Cloud OAuth2,您可以轻松地将OAuth2认证和授权集成到您的应用程序中,从而实现更安全的分布式系统。 ### 回答2: Spring Cloud OAuth2认证是一种集成了OAuth2标准的身份验证框架,它可以用来保护和授权API访问,也可以用于单点登录和资源服务器保护。 与传统的身份验证方式相比,Spring Cloud OAuth2认证的主要优势在于其更为灵活的授权方式,可以支持不同的授权流程,例如授权码、简化流程和密码凭据流程。此外,Spring Cloud OAuth2认证还提供了可定制化的身份验证流程和接口,开发人员可以根据业务需求对其进行定制。 在使用Spring Cloud OAuth2认证时,需要先配置授权服务器,包括定义客户端类型、授权模式等。接着,在客户端应用程序中会添加安全配置,以确保请求可以被身份验证和授权。最后,当用户请求受保护的API时,Spring Cloud OAuth2认证将使用令牌管理器验证用户身份和访问权限,并允许或拒绝请求。 总的来说,Spring Cloud OAuth2认证为开发人员提供了一种安全、灵活和可定制的身份验证和授权框架,可以帮助企业保护他们的API和资源服务器,同时也可以提高用户体验和安全性。 ### 回答3: Spring Cloud OAuth2是基于OAuth2协议的分布式认证和授权框架。OAuth2协议是一种在客户端与服务端之间进行授权委托的协议,它通过令牌(Token)的方式将授权信息传递给客户端或第三方服务。 Spring Cloud OAuth2提供了一系列组件,包括OAuth2认证服务,资源服务器以及客户端工具,为企业微服务架构提供了安全可靠的认证和授权机制。下面分别介绍OAuth2认证服务、资源服务器以及客户端工具的功能和作用。 1. OAuth2认证服务: OAuth2认证服务是指用于管理令牌(Token)的服务。在Spring Cloud OAuth2中,认证服务可以基于内存存储令牌,也可以基于数据库存储令牌。认证服务主要负责令牌的颁发以及令牌的刷新和撤销。 2. 资源服务器: 资源服务器是指受保护的服务提供方,它需要对请求者的身份进行认证和授权才能够提供服务。Spring Cloud OAuth2提供了一套成熟的资源服务管理机制,通过oauth2-resource注解来声明当前服务是资源服务。 3. 客户端工具: 客户端工具是指用于进行OAuth2认证和授权的工具,可以是Web应用,也可以是移动应用。在Spring Cloud OAuth2中,客户端工具可以通过注解的方式来进行OAuth2认证和授权,简化了客户端代码实现过程。 通过Spring Cloud OAuth2框架的应用,我们可以实现企业级的分布式认证和授权,规范了服务之间的安全通信,助力于微服务架构的安全发展。同时也提供了多种服务集成方案,如内存存储和数据库存储等,方便开发者根据实际需求选择合适的方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悠然予夏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值