OAuth2.0系列之簡化模式實踐教程(三)


@

OAuth2.0系列博客:

1、簡化模式簡介

1.1 前言簡介

上一篇文章中我們學習了OAuth2的一些基本概念,對OAuth2有了基本的認識,接着學習OAuth2.0授權模式中的簡化模式

ps:OAuth2.0的授權模式可以分為:

  • 授權碼模式(authorization code)
  • 簡化模式(implicit)
  • 密碼模式(resource owner password credentials)
  • 客戶端模式(client credentials)

簡化模式(implicit grant type)不通過第三方應用程序的服務器,直接在瀏覽器中向認證服務器申請令牌,跳過了"授權碼"這個步驟,因此稱簡化模式。簡化模式是相對於授權碼模式而言的

1.2 授權流程圖

官網圖片:
在這里插入圖片描述

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

  • (A):客戶端攜帶client_id、redirect_uri,中間通過代理者訪問授權服務器,如果已經登錄過會直接返回redirect_uri,沒有登錄過就跳轉到登錄頁面
  • (B)授權服務器對客戶端進行身份驗證(通過用戶代理,讓用戶輸入用戶名和密碼)
  • (C)授權通過,會重定向到redirect_uri並攜帶授權碼token作為uri參數
  • (D)客戶端攜帶授權碼訪問資源服務器
  • (E)驗證token通過,返回資源

從調接口方面,簡單來說:

  • 第一步:訪問授權,要傳client_id:客戶端id,redirect_uri:重定向uri,response_type為token,scope是授權范圍,state是其它自定義參數

http://localhost:8888/oauth/authorize?client_id=cms&redirect_uri=http://localhost:8084/callback&response_type=token&scope=read&state=123

  • 第二步:授權通過,會重定向到redirect_uri,access_token碼會作為它的參數

http://localhost:8084/callback#access_token=${accept_token}&token_type=bearer&state=123&expires_in=120

  • 第三步:拿到acceptToken之后,就可以直接訪問資源

2、例子實踐

2.1 實驗環境准備

  • IntelliJ IDEA
  • Maven3.+版本
    新建SpringBoot Initializer項目,可以命名implicit
    在這里插入圖片描述

在這里插入圖片描述
主要是想引入:

 <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.implicit.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 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(IMPLICIT );
    }

    @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.implicit.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()
                // 配置登出頁面
                .and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
                .and().authorizeRequests().antMatchers("/oauth/**", "/login/**", "/logout/**").permitAll()
                // 其余所有請求全部需要鑒權認證
                .anyRequest().authenticated()
                // 關閉跨域保護;
                .and().csrf().disable();
    }

}

寫個測試接口:

package com.example.oauth2.implicit.web.controller;

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

import java.security.Principal;

/**
 * <pre>
 *  用戶信息控制類
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改記錄
 *    修改后版本:     修改人:  修改日期: 2020/06/11 14:09  修改內容:
 * </pre>
 */
@RestController
@RequestMapping("/api")
public class UserController {

    @GetMapping("/userinfo")
    public Principal getCurrentUser(Principal principal) {
        return principal;
    }
}

2.5 功能簡單測試

訪問授權鏈接,在瀏覽器訪問就可以,簡化模式response_type參數傳token:

http://localhost:8888/oauth/authorize?client_id=cms&redirect_uri=http://127.0.0.1:8084/cms/login&response_type=token&scope=all

因為沒登錄,所以會返回SpringSecurity的默認登錄頁面,具體代碼是 http .formLogin().permitAll();,如果要彈窗登錄的,可以配置http.httpBasic();,這種配置是沒有登錄頁面的,自定義登錄頁面可以這樣配置http.formLogin().loginPage("/login").permitAll()

如圖,輸入SpringSecurity配置的靜態賬號密碼:nicky/123
在這里插入圖片描述

登錄成功,返回redirect_uri,拿到token
http://127.0.0.1:8084/cms/login#access_token=9b021755-7df3-48a4-bf58-40815a4dcc9b&token_type=bearer&expires_in=119

拿到token直接去調業務接口:
http://localhost:8888/api/userinfo?access_token=9b021755-7df3-48a4-bf58-40815a4dcc9b

{
    "authorities":[
        {
            "authority":"ROLE_admin"
        }
    ],
    "details":{
        "remoteAddress":"0:0:0:0:0:0:0:1",
        "sessionId":null
    },
    "authenticated":true,
    "principal":{
        "password":null,
        "username":"nicky",
        "authorities":[
            {
                "authority":"ROLE_admin"
            }
        ],
        "accountNonExpired":true,
        "accountNonLocked":true,
        "credentialsNonExpired":true,
        "enabled":true
    },
    "credentials":null,
    "name":"nicky"
}

例子代碼下載:code download


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM