• SpringBoot2 整合OAuth2组件,模拟第三方授权访问


    本文源码:GitHub·点这里 || GitEE·点这里

    一、模式描述

    授权服务

    验证第三方服务的身份,验证邮箱用户的身份,记录和管理认证Token,为资源服务器提供Token校验。场景:第三方网站借助用户的邮箱登录,并访问邮箱账户的基础信息,头像、名称等。

    资源服务

    第三方服务通过邮箱账户登录后需要获取的一些信息,即理解为资源,存储邮箱账户的数据资源。

    第三方服务

    即借助邮箱用户的账户,快速登录第三个服务,免去繁杂的注册流程,有助于快速积累新用户。

    交互流程

    第三方服务给用户开放快速邮箱登录功能,引导用户调到邮箱认证服务,通过认证后返回身份令牌到第三方服务,第三方服务携带令牌访问邮箱的资源服务,获取一些基本的邮箱用户信息。

    二、项目配置管理

    1、案例结构

    核心依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security.oauth</groupId>
        <artifactId>spring-security-oauth2</artifactId>
        <version>2.1.3.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    

    这里有两个核心组件依赖:OAuth2组件和Security组件。

    模块划分

    • auth-server:授权服务
    • resource-server:资源服务器
    • third-server:第三个服务

    2、配置描述

    【授权服务】

    OAuth2配置

    这里的配置管理的是第三方的授权流程和发放给第三方的身份证明ClientID和密码,实际的场景就是第三方借助邮箱账号登录,首先就是向邮箱管理方提供材料,获取访问邮箱服务的身份证明,然后才能对接开放服务,这种模式在第三方对接业务中很常见。

    /**
     * 模拟第三方授权配置
     */
    @EnableAuthorizationServer
    @Configuration
    public class AuthConfig extends AuthorizationServerConfigurerAdapter {
    
        @Resource
        ClientDetailsService clientDetailsService;
    
        /**
         * 资源服务器校验Token
         */
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) {
            security.checkTokenAccess("permitAll()").allowFormAuthenticationForClients();
        }
        /**
         * 第三方客户端请求配置,和资源服务访问的配置,不设置默认都可以访问,提供默认回调地址
         */
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                    .withClient("third01")
                    .secret(new BCryptPasswordEncoder().encode("third01"))
                    .resourceIds("resource-01")
                    .authorizedGrantTypes("authorization_code","refresh_token")
                    .scopes("all")
                    .redirectUris("http://localhost:8082/notify.html");
        }
        /**
         * 配置访问端点
         */
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
            endpoints.authorizationCodeServices(authorizationCodeServices()).tokenServices(tokenServices());
        }
        /**
         * 内存管理
         */
        @Bean
        AuthorizationCodeServices authorizationCodeServices() {
            return new InMemoryAuthorizationCodeServices();
        }
        /**
         * Token管理规则
         */
        @Bean
        AuthorizationServerTokenServices tokenServices() {
            DefaultTokenServices services = new DefaultTokenServices();
            services.setClientDetailsService(clientDetailsService);
            services.setSupportRefreshToken(true);
            services.setTokenStore(tokenStore());
            services.setAccessTokenValiditySeconds(3600);
            services.setRefreshTokenValiditySeconds(3600*7);
            return services;
        }
        @Bean
        TokenStore tokenStore() {
            return new InMemoryTokenStore();
        }
    }
    

    通常需要数据库存储第三方信息,可以到第OAuth2开源项目中,获取表结构放到本地数据库中,然后这里换成数据源加载模式即可,简单的流程管理都在源码里写了SQL语句,数据源引入即可。

    Security配置

    /**
     * 模拟本地用户配置
     */
    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        /**
         * 密码加密方式
         */
        @Bean
        public PasswordEncoder passwordEncoder(){
            return new BCryptPasswordEncoder();
        }
        /**
         * 内存中虚拟用户和角色
         */
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication()
                    .withUser("user")
                    .password(new BCryptPasswordEncoder().encode("123456"))
                    .roles("user");
        }
        /**
         * 表单登录
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable().formLogin();
        }
    }
    

    基于这里的配置管理邮箱用户的认证流程,例如使用邮箱账号密码登录验证,判断授权是否成立,这里管理的是服务本地的邮箱账号,基于数据源存储数据在下面案例中都有。

    关于Spring框架中安全认证的相关的几个组件,在使用OAuth2之前可以先了解一下。

    【资源服务】

    主要功能有三块,配置第三方携带的Token身份令牌校验机制,即访问授权服务校验接口,这里是OAuth2自定义好的接口;配置resourceId资源服务的编号,用来控制第三个服务能访问的资源服务范围,属于大的权限点控制;模拟校验用户的Role角色,较精细的控制权限。

    /**
     * 资源服务管理配置
     */
    @Configuration
    @EnableResourceServer
    public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
        /**
         * Token令牌校验
         */
        @Bean
        RemoteTokenServices tokenServices() {
            RemoteTokenServices services = new RemoteTokenServices();
            services.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");
            services.setClientId("third01");
            services.setClientSecret("third01");
            return services;
        }
        /**
         * 服务资源ID配置
         */
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.resourceId("resource-01").tokenServices(tokenServices());
        }
        /**
         * 模拟用户权限规则
         */
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers("/user/**").hasRole("user")
                    .anyRequest().authenticated();
        }
    }
    

    【第三方服务】

    主要提供两个流程的模拟:请求授权服务获取身份令牌;携带身份令牌请求资源服务获取数据。这里则是授权码回调接口的处理方式。

    @Controller
    public class NotifyController {
    
        private static final Logger LOG = LoggerFactory.getLogger(NotifyController.class);
    
        @Resource
        private RestTemplate restTemplate;
    
        @GetMapping("/notify.html")
        public String notify(String code, Model model) {
            if (code != null) {
                MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
                map.add("code", code);
                map.add("client_id", "third01");
                map.add("client_secret", "third01");
                map.add("redirect_uri", "http://localhost:8082/notify.html");
                map.add("grant_type", "authorization_code");
                Map<String,String> resp = restTemplate.postForObject("http://localhost:8080/oauth/token", map, Map.class);
                String accessToken = resp.get("access_token");
                LOG.info("身份令牌:{}",accessToken);
                HttpHeaders headers = new HttpHeaders();
                headers.add("Authorization", "Bearer " + accessToken);
                HttpEntity<Object> httpEntity = new HttpEntity<>(headers);
                ResponseEntity<String> entity = restTemplate.exchange("http://localhost:8081/user/resource", HttpMethod.GET, httpEntity, String.class);
                model.addAttribute("notifyMsg", entity.getBody());
            }
            return "notify";
        }
    }
    

    三、测试流程

    通过上述测试流程,对比常见的第三方登录机制,理解OAuth2的授权码模式。

    四、源代码地址

    GitHub·地址
    https://github.com/cicadasmile/middle-ware-parent
    GitEE·地址
    https://gitee.com/cicadasmile/middle-ware-parent
    

    推荐阅读:编程体系整理

    序号 项目名称 GitHub地址 GitEE地址 推荐指数
    01 Java描述设计模式,算法,数据结构 GitHub·点这里 GitEE·点这里 ☆☆☆☆☆
    02 Java基础、并发、面向对象、Web开发 GitHub·点这里 GitEE·点这里 ☆☆☆☆
    03 SpringCloud微服务基础组件案例详解 GitHub·点这里 GitEE·点这里 ☆☆☆
    04 SpringCloud微服务架构实战综合案例 GitHub·点这里 GitEE·点这里 ☆☆☆☆☆
    05 SpringBoot框架基础应用入门到进阶 GitHub·点这里 GitEE·点这里 ☆☆☆☆
    06 SpringBoot框架整合开发常用中间件 GitHub·点这里 GitEE·点这里 ☆☆☆☆☆
    07 数据管理、分布式、架构设计基础案例 GitHub·点这里 GitEE·点这里 ☆☆☆☆☆
    08 大数据系列、存储、组件、计算等框架 GitHub·点这里 GitEE·点这里 ☆☆☆☆☆
  • 相关阅读:
    转载 | CSS文本溢出显示省略号
    转载 | CSS书写顺序
    转载 | CSS布局大全
    threejs sprite 制作标签
    typeScript 中的类
    Es5中的类
    typeScript中的函数
    websocket在vue项目中的使用
    typeScript中的变量数据类型
    echarts 中的1/4圆环行图的使用
  • 原文地址:https://www.cnblogs.com/cicada-smile/p/14170971.html
Copyright © 2020-2023  润新知