zoukankan      html  css  js  c++  java
  • Spring Cloud进阶之路 | 八:授权服务(Spring Cloud Oauth2)

    ​转载请注明作者及出处:

    作者:银河架构师

    原文链接:https://www.cnblogs.com/luas/p/12201382.html

    oauth2简介

    OAuth(开放授权)是一个开放标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方应用或分享他们数据的所有内容。

    OAuth 2 定义了四种 Grant Type,授权码模式(authorization code)、简化模式(implicit)、密码模式(Password)、客户端模式(client credentials),每一种都有适用的应用场景。

    运行流程如下所示:

    至于每种模式如何运行,需要什么参数,本文不再赘述,大家可自行查阅相关资料,做到烂熟于心。

    微服务安全策略

    在传统单体Web应用架构中,身份认证从来都不是问题,通过Spring Security或者Shiro,配合session,可以很方便的解决身份认证和鉴权的问题。

    随着不断扩大的业务需求,在传统单体架构捉襟见肘的时候,分布式架构应运而生,分布式架构强调的是服务化以及服务的分散化,大大地提升了系统的可靠性和响应速度。

    可是,技术并没有因此而停滞不前,微服务架构又出现了。微服务架构更强调服务的专业化、精细化,更加强调单一职责、轻量级通信(HTTP)、独立性并且进程隔离。

    随着应用架构的改变,身份认证的方式也在发生变化,为了适应架构的变化、需求的变化,身份认证与鉴权方案也需要不断的变革。

    抛开鉴权不谈,先说一下身份认证。David Borsos 在伦敦的微服务大会上提出的四种方案,单点登录(SSO)、分布式session、客户端 Token、客户端 Token 与 API 网关结合。其中,后两者颇受开发者欢迎,也很适合微服务架构。然而,无论后两者中的哪一种方案,均需一个独立的授权服务器配合,才能发放token,进而协助资源服务器进行身份认证。

    搭建授权服务器


    基于Spring Cloud Oauth2框架搭建基本的授权服务。

    pom文件

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion><parent>
            <groupId>com.luas.cloud</groupId>
            <artifactId>java-boot-parent-2.1</artifactId>
            <version>1.0.0-SNAPSHOT</version>
            <relativePath>../../java-boot-parent-2.1</relativePath>
        </parent><groupId>com.luas.xmall</groupId>
        <artifactId>xmall-auth</artifactId>
        <version>1.0.0-SNAPSHOT</version><name>xmall-auth</name>
        <description>xmall authorization center</description><properties>
        </properties><dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency><dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-oauth2</artifactId>
            </dependency><!-- nacos cloud -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            </dependency><dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency><dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies><build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build></project>

    动态配置

    同前文所述,bootstrap.yml中配置nacos发现地址、配置中心地址,实现服务注册及配置远程获取、动态刷新。后续此类配置均相同,不再赘述。

    spring:
      application:
        name: xmall-auth
      cloud:
        nacos:
          config:
            server-addr: 127.0.0.1:8848
            file-extension: yml
          discovery:
            server-addr: 127.0.0.1:8848

    AuthorizationServer

    AuthorizationServer主要配置有三大类,安全配置、端点配置、客户端配置

    安全配置主要针对授权服务器端点的访问策略、认证策略、加密方式等进行配置。

    端点配置主要配置授权服务器的token存储方式、token转换、端点增强、端点自定义、token授权、token生成等进行配置。

    客户端配置主要配置接入的客户端相关信息,如授权类型、授权范围、秘钥等内容。

    package com.luas.xmall.auth.configuration;
    ​
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    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.authentication.AuthenticationProvider;
    import org.springframework.security.authentication.ProviderManager;
    import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
    import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder;
    import org.springframework.security.oauth2.config.annotation.builders.JdbcClientDetailsServiceBuilder;
    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.InMemoryClientDetailsService;
    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.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
    ​
    import java.util.ArrayList;
    import java.util.List;
    ​
    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    ​
        @Autowired
        private PasswordEncoder passwordEncoder;
    ​
        @Autowired
        private UserDetailsService userDetailsService;
    ​
        @Autowired
        private AuthenticationManager authenticationManager;
    ​
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            security.
                    allowFormAuthenticationForClients()
                    .tokenKeyAccess("permitAll()")
                    .checkTokenAccess("permitAll()")
            ;
        }
    ​
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.tokenStore(new InMemoryTokenStore())
                    .authenticationManager(authenticationManager)
                    .userDetailsService(userDetailsService)
                    .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
        }
    ​
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            String client_secret = passwordEncoder.encode("123456");
    ​
            clients
                    .inMemory()
                    // admin,授权码认证、密码认证、客户端认证、简单认证、刷新token
                    .withClient("admin")
                    .secret(client_secret)
                    .resourceIds("xmall-auth", "xmall-product")
                    .scopes("server", "select")
                    .authorizedGrantTypes("authorization_code", "password", "refresh_token", "client_credentials", "implicit")
                    .redirectUris("http://www.baidu.com")
    ​
                    .and()
                    // client_1,密码认证、刷新token
                    .withClient("client_1")
                    .secret(client_secret)
                    .resourceIds("xmall-auth", "xmall-product")
                    .scopes("server", "select")
                    .authorizedGrantTypes("password", "refresh_token")
    ​
                    .and()
                    // client_2,客户端认证、刷新token
                    .withClient("client_2")
                    .secret(client_secret)
                    .resourceIds("xmall-auth", "xmall-product")
                    .scopes("server", "select")
                    .authorizedGrantTypes("client_credentials", "refresh_token");
        }
    ​
    }

    ResourceServer

    主要负责针对资源服务器的安全访问策略进行相关配置。

    为什么授权服务器还需要进行资源服务器相关配置?

    举一个最简单的例子。比如github,第三方接入之后,发起请求,用户跳转到github进行登录并授权,这时,接入方可以得到授权,但是,并不知道用户的具体信息,需要带着授权去询问github,当前用户的相关信息。

    github进行相关身份认证之后,将可授权的用户信息返回给接入方。这个获取当前用户相关信息的接口端点,是为资源。既然是资源,就要受资源服务器管辖,进而就需要进行资源服务器配置。

    package com.luas.xmall.auth.configuration;
    ​
    import org.springframework.context.annotation.Configuration;
    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;
    ​
    @Configuration
    @EnableResourceServer
    public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.resourceId("xmall-auth").stateless(true);
        }
    ​
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                    .requestMatchers()
                    .antMatchers("/oauth/user")
                    .and()
                    .authorizeRequests()
                    .anyRequest()
                    .authenticated();
        }
    }

    注意,用户相关信息的接口端点必须由ResourceServer安全策略拦截。

    SpringSecurity配置

    老套路了,毋庸置疑需要配置。可前两个都进行了SpringSecurity相关配置,为何还需要单独针对SpringSecurity再进行配置呢?

    答案之一就在于授权模式中的授权码模式,后续会出相关文章,详细说明这个问题。此外,还需配置UserDetailsService、PasswordEncoder等内容。

    package com.luas.xmall.auth.configuration;
    ​
    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.config.annotation.web.configuration.EnableWebSecurity;
    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.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.provisioning.InMemoryUserDetailsManager;
    ​
    import java.util.ArrayList;
    import java.util.List;
    ​
    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    ​
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .requestMatchers()
                    .anyRequest()
                    .and()
                    .formLogin()
                    .and()
                    .csrf().disable();
        }
    ​
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    ​
        @Bean(name = "userDetailsService")
        @Override
        public UserDetailsService userDetailsServiceBean() throws Exception {
            return createUserDetailsService();
        }
    ​
        @Override
        protected UserDetailsService userDetailsService() {
            return createUserDetailsService();
        }
    ​
        private UserDetailsService createUserDetailsService() {
            String password = passwordEncoder().encode("123456");
    ​
            List<UserDetails> users = new ArrayList<>();
    ​
            UserDetails user_admin = User.withUsername("admin").password(password).authorities("ADMIN", "USER").build();
            UserDetails user_1 = User.withUsername("user_1").password(password).authorities("ADMIN", "USER").build();
            UserDetails user_2 = User.withUsername("user_2").password(password).authorities("USER").build();
    ​
            users.add(user_admin);
            users.add(user_1);
            users.add(user_2);
    ​
            return new InMemoryUserDetailsManager(users);
        }
    ​
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    }

    用户信息端点

    为接入方提供用户信息查询端点,查询当前认证用户的相关信息。

    package com.luas.xmall.auth.controller;
    ​
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    ​
    import java.security.Principal;
    ​
    @RestController
    @RequestMapping("/oauth")
    public class UserController {
    ​
        @RequestMapping("/user")
        public Principal user(Principal user) {
            return user;
        }
    ​
    }

    启动

    端口配置为7777,启动服务。

    授权


    本文使用Postman工具,模拟rest请求。

    访问http://localhost:7777/oauth/token,输入一众参数,诸如client_id、client_secret、grant_type等,点击Send,即出现access_token、refresh_token等信息。

    用户信息

    拿着上一步获取的授权,访问http://localhost:7777/oauth/user,查询当前用户的相关信息。

    注意,要添加授权token,本文采用header方式(header方式添加授权token,即在HttpHeader中添加header Authentication,内容为Bearer+空格+token),如下所示:

    授权服务器搭建成功!

    源码


    github

    https://github.com/liuminglei/SpringCloudLearning/tree/master/08

    gitee

    https://gitee.com/xbd521/SpringCloudLearning/tree/master/08

    微信搜索【银河架构师】,发现更多精彩内容。

    技术资料领取方法:关注公众号,回复微服务,领取微服务相关电子书;回复MK精讲,领取MK精讲系列电子书;回复JAVA 进阶,领取JAVA进阶知识相关电子书;回复JAVA面试,领取JAVA面试相关电子书,回复JAVA WEB领取JAVA WEB相关电子书。

  • 相关阅读:
    update 更改字段
    查看 links
    oracle 统计字段空值数
    查看oracle 数据库编码个格式
    oracle 数据库DBA权限
    一文弄懂 Golang 排序
    Golang Linux、Windows、Mac 下交叉编译
    go 简单封装数学运算包
    [Vue warn]: Unknown custom element: did you register the component correctly?
    深度图怎么看行情走势
  • 原文地址:https://www.cnblogs.com/luas/p/12201382.html
Copyright © 2011-2022 走看看