• OAuth2.0系列之客户端模式实践教程(五)


    @

    OAuth2.0系列博客:

    1、客户端模式简介

    1.1 前言简介

    上一篇文章中我们学习了OAuth2的一些基本概念,对OAuth2有了基本的认识,接着学习OAuth2.0授权模式中的客户端模式

    ps:OAuth2.0的授权模式可以分为:

    • 授权码模式(authorization code)
    • 简化模式(implicit)
    • 密码模式(resource owner password credentials)
    • 客户端模式(client credentials)

    客户端模式(client credentials):客户端模式(client credentials)适用于没有前端的命令行应用,即在命令行下请求令牌

    1.2 授权流程图

    官网图片:
    在这里插入图片描述

    • (A)客户端提供client_id等信息给授权服务器授权服务器身份验证
    • (B)授权通过,返回acceptToken给客户端

    从调接口方面,简单来说:

    http://localhost:8084/api/userinfo?access_token=${accept_token}

    2、例子实践

    2.1 实验环境准备

    • IntelliJ IDEA
    • Maven3.+版本
      新建SpringBoot Initializer项目,可以命名password
      在这里插入图片描述

    在这里插入图片描述
    主要是想引入:

     <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
     <!-- Spring Cloud Oauth2-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-oauth2</artifactId>
            </dependency>
            <!-- Spring Cloud Security-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-security</artifactId>
            </dependency>
    

    2.2 OAuth2.0角色

    前面的学习,我们知道了OAuth2.0主要包括如下角色,下面通过代码例子加深对理论的理解

    • 资源所有者(Resource Owner)
    • 用户代理(User Agent)
    • 客户端(Client)
    • 授权服务器(Authorization Server)
    • 资源服务器(Resource Server)

    生产环境、资源服务器和授权服务器一般是分开的,不过学习的可以放在一起

    定义资源服务器,用注解@EnableResourceServer;
    定义授权服务器,用注解@EnableAuthorizationServer;

    2.3 OAuth2.0配置类

    package com.example.oauth2.clientcredentials.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.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.token.TokenStore;
    import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
    
    
    /**
     * <pre>
     *     OAuth2.0配置类
     * </pre>
     *
     * <pre>
     * @author mazq
     * 修改记录
     *    修改后版本:     修改人:  修改日期: 2020/06/11 11:00  修改内容:
     * </pre>
     */
    @Configuration
    //开启授权服务
    @EnableAuthorizationServer
    public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
    
        @Autowired
        private AuthenticationManager authenticationManager;
    
        private static final String CLIENT_ID = "cms";
        private static final String SECRET_CHAR_SEQUENCE = "{noop}secret";
        private static final String SCOPE_READ = "read";
        private static final String SCOPE_WRITE = "write";
        private static final String TRUST = "trust";
        private static final String USER ="user";
        private static final String ALL = "all";
        private static final int ACCESS_TOKEN_VALIDITY_SECONDS = 2*60;
        private static final int FREFRESH_TOKEN_VALIDITY_SECONDS = 2*60;
        // 密码模式授权模式
        private static final String GRANT_TYPE_PASSWORD = "password";
        //授权码模式
        private static final String AUTHORIZATION_CODE = "authorization_code";
        //refresh token模式
        private static final String REFRESH_TOKEN = "refresh_token";
        //简化授权模式
        private static final String IMPLICIT = "implicit";
        //客户端模式
        private static final String CLIENT_CREDENTIALS="client_credentials";
        //指定哪些资源是需要授权验证的
        private static final String RESOURCE_ID = "resource_id";
    
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients
                    // 使用内存存储
                    .inMemory()
                    //标记客户端id
                    .withClient(CLIENT_ID)
                    //客户端安全码
                    .secret(SECRET_CHAR_SEQUENCE)
                    //为true 直接自动授权成功返回code
                    .autoApprove(true)
                    .redirectUris("http://127.0.0.1:8084/cms/login") //重定向uri
                    //允许授权范围
                    .scopes(ALL)
                    //token 时间秒
                    .accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
                    //刷新token 时间 秒
                    .refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS)
                    //允许授权类型
                    .authorizedGrantTypes(CLIENT_CREDENTIALS );
        }
    
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            // 使用内存保存生成的token
            endpoints.authenticationManager(authenticationManager).tokenStore(memoryTokenStore());
        }
    
        /**
         * 认证服务器的安全配置
         *
         * @param security
         * @throws Exception
         */
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            security
                    //.realm(RESOURCE_ID)
                    // 开启/oauth/token_key验证端口认证权限访问
                    .tokenKeyAccess("isAuthenticated()")
                    //  开启/oauth/check_token验证端口认证权限访问
                    .checkTokenAccess("isAuthenticated()")
                    //允许表单认证
                    .allowFormAuthenticationForClients();
        }
    
        @Bean
        public TokenStore memoryTokenStore() {
            // 最基本的InMemoryTokenStore生成token
            return new InMemoryTokenStore();
        }
    
    }
    
    
    

    2.4 Security配置类

    为了测试,可以进行简单的SpringSecurity

    package com.example.oauth2.clientcredentials.config;
    
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.annotation.Order;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.builders.WebSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    
    /**
     * <pre>
     *  SpringSecurity配置类
     * </pre>
     *
     * <pre>
     * @author mazq
     * 修改记录
     *    修改后版本:     修改人:  修改日期: 2020/06/11 11:23  修改内容:
     * </pre>
     */
    @Configuration
    @EnableWebSecurity
    @Order(1)
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {    //auth.inMemoryAuthentication()
            auth.inMemoryAuthentication()
                    .withUser("nicky")
                    .password("{noop}123")
                    .roles("admin");
        }
    
        @Override
        public void configure(WebSecurity web) throws Exception {
            //解决静态资源被拦截的问题
            web.ignoring().antMatchers("/asserts/**");
            web.ignoring().antMatchers("/favicon.ico");
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http   // 配置登录页并允许访问
                    //.formLogin().permitAll()
                    // 配置Basic登录
                    //.and().httpBasic()
                    // 配置登出页面
                    .logout().logoutUrl("/logout").logoutSuccessUrl("/")
                    // 配置允许访问的链接
                    .and().authorizeRequests().antMatchers("/oauth/**", "/login/**", "/logout/**","/api/**").permitAll()
                    // 其余所有请求全部需要鉴权认证
                    .anyRequest().authenticated()
                    // 关闭跨域保护;
                    .and().csrf().disable();
        }
    
    }
    
    
    
    

    2.5 功能简单测试

    接口测试,要用POST方式,在postman测试,response_type参数传client_credentials:

    http://localhost:8888/oauth/token?client_id=cms&client_secret=secret&grant_type=client_credentials&scope=all

    在这里插入图片描述

    注意配置一下请求头的授权参数,username即client_id,password即client_secret
    在这里插入图片描述

    代码方式请求,可以进行如下封装,即进行base64加密

    HttpHeaders headers = new HttpHeaders();
            byte[] key = (clientId+":"+clientSecret).getBytes();
            String authKey = new String(Base64.encodeBase64(key));
            LOG.info("Authorization:{}","Basic "+authKey);
            headers.add("Authorization","Basic "+authKey);
    

    拿到token直接去调业务接口:
    http://localhost:8888/api/userinfo?access_token=61b113f3-f1e2-473e-a6d7-a0264bfdfa8d

    例子代码下载:code download

  • 相关阅读:
    搞懂:MVVM模型以及VUE中的数据绑定数据劫持发布订阅模式
    搞懂:前端跨域问题JS解决跨域问题VUE代理解决跨域问题原理
    搞懂js中小数运算精度问题原因及解决办法
    前端对base64编码的理解,原生js实现字符base64编码
    【置顶】我的联系方式
    浅析Block闭包
    再探NSString
    OC属性与实例变量
    IsEqual与Hash个人理解
    Copy与mutableCopy的个人理解
  • 原文地址:https://www.cnblogs.com/mzq123/p/13094836.html
Copyright © 2020-2023  润新知