Spring Security OAuth2 Demo —— 隱式授權模式(Implicit)


本文可以轉載,但請注明出處https://www.cnblogs.com/hellxz/p/oauth2_impilit_pattern.html

寫在前面

在文章OAuth 2.0 概念及授權流程梳理 中我們談到OAuth 2.0的概念與流程,上一篇文章Spring Security OAuth2 Demo —— 授權碼模式簡單演示了OAuth2的授權碼模式流程,本文繼續整理隱式授權模式相關內容

寫文不易,如有錯誤,請在評論區指出,謝謝合作

本文目標

使用相對簡易的代碼演示隱式授權模式的流程,讓其流程更加清晰易懂

隱式授權模式流程回顧

隱式授權模式要求:用戶登錄並對第三方應用進行授權,直接返回訪問token,通過token訪問資源

相比授權碼模式,它少了一次授權碼的頒發與客戶端使用授權碼換取token的過程

隱式授權模式適用場景

適用場景有以下幾個條件:

  • 用戶參與:使用隱式授權需要與用戶交互,用戶對授權服務器進行登錄與授權
  • 單頁應用:SPA前端,沒有后端或者后端屬於授權方
  • 客戶端密碼:訪問授權時,不需要帶第三方應用secret,前提是資源服務校驗token使用的client信息與客戶端(第三方應用)不同,且配置了secret
  • 前端:必須要有前端,否則無法使用授權功能
  • 客戶端后端:Options,僅當應用前后端不分離MVC場景
  • 資源所屬方:授權方

Demo結構

主要還是兩個角色,授權服務器與資源服務器兩個模塊,另外與其他幾個demo一樣,在父項目中包含一個說明文檔

本文以及后續文章的demo均放在GitHub上,歡迎大家Star & Fork,源碼地址:https://github.com/hellxz/spring-security-oauth2-learn

Maven依賴

        <!--Spring Security-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--Spring Boot Starter Web 所有demo均使用web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Spring Security OAuth2 -->
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>${spring-security-oauth2.version}</version>
        </dependency>

搭建授權服務器

項目啟動類不多說,直接貼代碼,講講主要內容

先說下SecurityConfig

package com.github.hellxz.oauth2.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.Collections;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // @formatter: off
        auth.inMemoryAuthentication()
                .withUser("hellxz")
                .password(passwordEncoder().encode("xyz"))
                .authorities(Collections.emptyList());
        // @formatter: on
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated() //所有請求都需要通過認證
                .and()
                .httpBasic() //Basic提交
                .and()
                .csrf().disable(); //關跨域保護
    }
}

參考了上文的話,這里基本上沒有什么變化,除了開啟web安全外,重寫了認證管理器的用戶提供部分、簡單配置了所有資源都需要認證

授權服務主要配置AuthorizationConfig

package com.github.hellxz.oauth2.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.password.PasswordEncoder;
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.AuthorizationServerSecurityConfigurer;

//授權服務器配置
@Configuration
@EnableAuthorizationServer //開啟授權服務
public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //允許表單提交
        security.allowFormAuthenticationForClients()
                .checkTokenAccess("permitAll()"); //參數與security訪問控制一致
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // @formatter: off
        clients.inMemory()
                .withClient("client-a") //client端唯一標識
                    .authorizedGrantTypes("implicit") //授權模式標識
                    .accessTokenValiditySeconds(120) //訪問令牌的有效期,這里設置120s
                    .scopes("read_user_info") //作用域
                    .resourceIds("resource1") //資源id
                    .redirectUris("http://localhost:9001/callback") //回調地址
                    .and()
                .withClient("resource-server") //資源服務器校驗token時用的客戶端信息,僅需要client_id與密碼
                    .secret(passwordEncoder.encode("test"));
        // @formatter: on
    }
}

因為最復雜的授權碼已經有講過了,這里簡單說下,授權配置除了開啟授權服務器,並重寫認證服務器安全配置(接收客戶端提交請求部分)允許客戶端進行表單提交;另外配置了一個客戶端的信息,包含其標識id、授權模式標識、令牌有效期、回調地址這幾個必要的配置;

為了更清晰地區分第三方應用的客戶端與資源服務器的客戶端,這里額外配置了資源服務的客戶端信息

測試授權服務器

  • 獲取token

瀏覽器訪問地址:http://localhost:8080/oauth/authorize?client_id=client-a&redirect_uri=http://localhost:9001/callback&response_type=token&scope=read_user_info

請求參數列表:

  • client_id=客戶端id
  • redirect_uri=回調url 一定要與授權服務器配置保持一致,否則得不到授權碼
  • response_type=token 簡化模式必須是token
  • scope=作用域 與授權服務器配置保持一致
  • state=自定義串(可選)

返回響應會回調我們之前輸入的回調地址,包含access_token和token類型及過期時間

搭建資源服務器

資源服務器也不復雜,一個資源服務器配置類,一個controller、一個vo,還有啟動類(這里就不貼了,詳見源碼)

ResourceController主要接收用戶傳來的用戶名,返回一個json串,這里用標准錯誤輸出高亮了下登錄用戶信息

package com.github.hellxz.oauth2.web.controller;

import com.github.hellxz.oauth2.web.vo.UserVO;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ResourceController {

    @GetMapping("/user/{username}")
    public UserVO user(@PathVariable String username){
        System.err.println(SecurityContextHolder.getContext().getAuthentication());
        return new UserVO(username, username + "@foxmail.com");
    }
}

UserVO

package com.github.hellxz.oauth2.web.vo;

public class UserVO {
    private String username;
    private String email;

    public UserVO(String username, String email) {
        this.username = username;
        this.email = email;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

資源服務器配置類ResourceConfig

package com.github.hellxz.oauth2.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
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;

@Configuration
@EnableResourceServer
public class ResourceConfig extends ResourceServerConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Primary
    @Bean
    public RemoteTokenServices remoteTokenServices() {
        final RemoteTokenServices tokenServices = new RemoteTokenServices();
        tokenServices.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");
        //這里的clientId和secret對應資源服務器信息,授權服務器處需要配置
        tokenServices.setClientId("resource-server");
        tokenServices.setClientSecret("test");
        return tokenServices;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        //設置創建session策略
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        //@formatter:off
        //所有請求必須授權
        http.authorizeRequests()
                .anyRequest().authenticated();
        //@formatter:on
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId("resource1").stateless(true);
    }
}

資源服務器相對授權服務器更簡單,僅需要開啟EnableResourceServer,實現HttpSecurity配置、ResourceServerSecurityConfigurer配置 和 校驗token的配置,這里使用遠程調用授權服務器的做法;

需要注意的是區分資源服務器client信息和第三方應用客戶端信息,之前這里有些模糊,直到著此文時方才發現這兩者應區分(隱式授權可以不用密碼啊,如果第三方應用等於資源服務器client,在不設置client_secret情況下,會校驗失敗,無法訪問資源)

一般而言,校驗token的配置如果是資源服務器自己校驗,則需要在configure(ResourceServerSecurityConfigurer resources)這個方法中添加token存儲(tokenStore)的位置等信息

使用token訪問資源

結束

最近比較忙,抽時間整理代碼時發現:我對OAuth2的資源服務器與授權服務器的client配置有些模糊,現在已經清晰多了,並且及時修改了demo。如果本文對你有幫助,歡迎點推薦,Github點Star :happy:

OAuth2系列demo倉庫地址:https://github.com/hellxz/spring-security-oauth2-learn

紙上得來終覺淺,覺知此事要躬行。願大家共勉

本文可以轉載,但請注明出處https://www.cnblogs.com/hellxz/p/oauth2_impilit_pattern.html


免責聲明!

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



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