手把手OAuth2授权码模式(Authorization Code)

手把手OAuth2授权码模式(Authorization Code),第1张

手把手入门OAuth2授权码模式(Authorization Code) 1.简单介绍

OAuth2 授权码模式模式基本上是用用户凭证获取token 后来获取资源的访问权限。其交互步骤如下图:

交互过程如下:

  1. 用户在客户端程序上 *** 作某些功能希望从资源服务器获取数据
  2. 客户端程序重定向浏览器请求到授权服务器要求授权,在重定向之前客户端程序会给授权服务器传递一个参数作为回调地址
  3. 授权服务器请求用户同意,这个步骤一般需要用户先登录,如果已经登录则可能d出一个交互页面请求用户同意授权
  4. 用户决定同意授权
  5. 授权服务器重定向到第二步的回调地址,并且在URL 后附带一个Authorization Code,这个Authorization Code 是明文传递给客户端程序的,所以不安全
  6. 客户端程序用 Authorization Code 和 认证的密钥获取 access token
  7. 客户端程序用access token 访问资源服务器(也就是实际的业务接口)
  8. 资源服务器返回数据给客户端程序

交互图如下

2.授权服务器

Spring Authorization Server 是一个spring 专门处理授权服务的项目,我们构建授权服务器就是基于它。 我们先一步一步 *** 作完成后再看整个流程。

2.1 建立基本项目结构

整体项目结构如下

创建一个maven 项目oauth2-auth-server
pom.xml 如下:
源码地址 :细节请参考项目源码


<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.0modelVersion>
	<parent>
		<groupId>org.springframework.bootgroupId>
		<artifactId>spring-boot-starter-parentartifactId>
		<version>2.6.7version>
		<relativePath /> 
	parent>
	<groupId>com.xuegroupId>
	<artifactId>oauth2-auth-serverartifactId>
	<version>2022.04.28version>
	<name>oauth2-auth-servername>
	<description>spring Authorization Server Demo project for Spring Bootdescription>
	<properties>
		<java.version>17java.version>
		<p6spy>3.9.1p6spy>
	properties>
	<dependencies>

		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>

		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-data-jpaartifactId>
		dependency>

		<dependency>
			<groupId>org.springframework.securitygroupId>
			<artifactId>spring-security-oauth2-authorization-serverartifactId>
			<version>0.2.3version>
		dependency>
		<dependency>
			<groupId>p6spygroupId>
			<artifactId>p6spyartifactId>
			<version>${p6spy}version>
		dependency>


		<dependency>
			<groupId>com.h2databasegroupId>
			<artifactId>h2artifactId>
			<scope>runtimescope>
		dependency>
		<dependency>
			<groupId>mysqlgroupId>
			<artifactId>mysql-connector-javaartifactId>
			<scope>runtimescope>
		dependency>
		

		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-testartifactId>
			<scope>testscope>
		dependency>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-devtoolsartifactId>
			<scope>runtimescope>
			<optional>trueoptional>
		dependency>

	dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.bootgroupId>
				<artifactId>spring-boot-maven-pluginartifactId>
			plugin>
		plugins>
	build>

project>

注意 spring-security-oauth2-authorization-server这个依赖是最重要的
项目的配置文件application.yml如下
源码地址 :[细节请参考项目源码]

#https://docs.spring.io/spring-boot/docs spring文档
# https://docs.spring.io/spring-boot/docs/2.6.3/reference/html/application-properties.html#application-properties
server:
    port: 9000
# 默认激活dev配置
spring:
  profiles:
    active: "dev"
    #active: "prod"
    group:
      "dev": "common,jpa,h2,dev"
      "prod": "common,jpa,mysql,prod"
  
---
spring:
  config:
    activate:
      on-profile: "mysql"
jdbc:
  database: mysql
  init-mode: ALWAYS
  driver-class-name: com.mysql.cj.jdbc.Driver

---
spring:
  config:
    activate:
      on-profile: "h2"
  h2:
    console:
      #数据库控制台 http://localhost:9000/h2-console/login.jsp  
      path: /h2-console
      enabled: true
      setting:
         web-allow-others: true
jdbc:
   database: h2
   init-mode: EMBEDDED
   driver-class-name: org.h2.Driver
   

---
spring:
  config:
    activate:
      on-profile: "dev"
  
jdbc:
   #是否打印sql,true 或false
   show-sql: true
   url: mem:db1;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;TRACE_LEVEL_SYSTEM_OUT=2
   username: test
   password: 


---
spring:
  config:
    activate:
      on-profile: "prod"
  devtools:
       restart:
          enabled : false
jdbc:
   #是否打印sql,true 或false
   show-sql: false
   url: //localhost:8024/test?useSSL=false&useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8
   username: test
   password: 123456
---
spring:
  config:
    activate:
      on-profile: "common"
  application:
           name: auth-server
  logging:
     path : ./    
  datasource:
    driver-class-name: ${jdbc.driver-class-name-show-sql-${jdbc.show-sql}}
    url  : ${jdbc.url-show-sql${jdbc.show-sql}}
    username: ${jdbc.username}
    password: ${jdbc.password}
    #连接池池参数配置
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
        pool-name: hikariDataSourcePool
        connection-test-query: /* ping */  SELECT 1
        #connection-test-query: SELECT 1
        maximum-pool-size: 20
        minimum-idle: 5
        #默认30000  30s
        connection-timeout: 10000
        #默认5000
        validation-timeout: 3000
        #默认true
        is-auto-commit: false
        #默认false
        is-read-only: false
    

jdbc:
    #不打印sql
    driver-class-name-show-sql-false: ${jdbc.driver-class-name}
    url-show-sql-false : jdbc:${jdbc.database}:${jdbc.url}
    #打印sql
    driver-class-name-show-sql-true : com.p6spy.engine.spy.P6SpyDriver
    url-show-sql-true : jdbc:p6spy:${jdbc.database}:${jdbc.url}

---
spring:
  config:
    activate:
      on-profile: "jpa"
  jpa:
    database: ${jdbc.database}
    show-sql: false
    hibernate:
      ddl-auto: update
    open-in-view: false

注意我们的服务运行在 9000 端口

我们把用户信息用JPA 保存到数据库中,所以先建立用户模型
User.java

package com.xue.pojo;

@Entity
@Table(name = "sys_user")
public class User implements UserDetails {

	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;

	private final String username;
	private final String password;
	private final String role;
    ... 代码太多请参考项目源码
}

UserRepository.java 用户DAO 层

public interface UserRepository extends CrudRepository<User, Long> {

  User findByUsername(String username);
  
}

SecurityConfig.java spring安全配置

@EnableWebSecurity
public class SecurityConfig {
	@Bean
	SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
		// @formatter:off
		return http
		          //设置了