OAuth2 Authorization Server

基于Spring Security 5 的 Authorization Server的写法

先看演示

OAuth2 Authorization Server

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelversion>
    <parent>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-parent</artifactid>
        <version>2.6.5</version>
        <relativepath> <!-- lookup parent from repository -->
    </relativepath></parent>
    <groupid>com.cjs.example.oauth2</groupid>
    <artifactid>cjs-authorization-server</artifactid>
    <version>0.0.1-SNAPSHOT</version>
    <name>cjs-authorization-server</name>
    <properties>
        <java.version>1.8</java.version>
        <docker.image.prefix>cjssso</docker.image.prefix>
    </properties>
    <dependencies>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-security</artifactid>
        </dependency>
        <dependency>
            <groupid>org.springframework.security</groupid>
            <artifactid>spring-security-oauth2-authorization-server</artifactid>
            <version>0.2.3</version>
        </dependency>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-thymeleaf</artifactid>
        </dependency>
        <dependency>
            <groupid>org.thymeleaf.extras</groupid>
            <artifactid>thymeleaf-extras-springsecurity5</artifactid>
        </dependency>
        <!-- https://www.webjars.org/documentation#springboot -->
        <dependency>
            <groupid>org.webjars</groupid>
            <artifactid>webjars-locator-core</artifactid>
            <version>0.50</version>
        </dependency>
        <dependency>
            <groupid>org.webjars</groupid>
            <artifactid>bootstrap</artifactid>
            <version>5.1.3</version>
        </dependency>
        <dependency>
            <groupid>org.webjars</groupid>
            <artifactid>jquery</artifactid>
            <version>3.6.0</version>
        </dependency>
        <dependency>
            <groupid>org.projectlombok</groupid>
            <artifactid>lombok</artifactid>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-test</artifactid>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupid>org.springframework.security</groupid>
            <artifactid>spring-security-test</artifactid>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupid>org.springframework.boot</groupid>
                <artifactid>spring-boot-maven-plugin</artifactid>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupid>org.projectlombok</groupid>
                            <artifactid>lombok</artifactid>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <!-- https://github.com/spotify/docker-maven-plugin -->
            <plugin>
                <groupid>com.spotify</groupid>
                <artifactid>docker-maven-plugin</artifactid>
                <version>1.2.2</version>
                <configuration>
                    <imagename>${docker.image.prefix}/${project.artifactId}</imagename>
                    <dockerdirectory>src/main/docker</dockerdirectory>
                    <resources>
                        <resource>
                            <targetpath>/</targetpath>
                            <directory>${project.build.directory}</directory>
                            <include>${project.build.finalName}.jar</include>
                        </resource>
                    </resources>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

application.yml


server:
  port: 9000
 servlet:
   context-path: /uaa
logging:
  level:
    root: debug

Security配置


package com.cjs.example.oauth2.config;

import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;

/**
 * @Author ChengJianSheng
 * @Date 2022/3/29
 */
@Configuration
public class OAuth2AuthorizationServerSecurityConfiguration {

    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        return http.formLogin(Customizer.withDefaults()).build();
    }

    @Bean
    @Order(2)
    public SecurityFilterChain standardSecurityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((authorize) -> authorize
                .antMatchers("/webjars/**", "/login", "/login-error").permitAll()
                .anyRequest().authenticated())
                .formLogin().loginPage("/login").failureUrl("/login-error");

        return http.build();
    }

    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient client1 = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("client-1")
                .clientSecret("$2a$10$zNvOs9Ex0cD794OyWwDIXutF7F4hHYjLuEwI.U30p3nNIyumfON42")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .redirectUri("http://127.0.0.1:8081/message/login/oauth2/code/mycustom")
                .redirectUri("http://127.0.0.1:8081/message/authorized")
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(false).build())
                .build();
        RegisteredClient client2 = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("client-2")
                .clientSecret("$2a$10$csxC.p5gNQbnpv8Mt5dqPevS66QL2USHERtQ8hEA1TWETIk1Zo3SS")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .redirectUri("http://www.baidu.com")
                .scope("message:read")
                .build();

        return new InMemoryRegisteredClientRepository(client1, client2);
    }

    @Bean
    public JWKSource jwkSource(KeyPair keyPair) {
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

        RSAKey rsaKey = new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();

        JWKSet jwkSet = new JWKSet(rsaKey);

        return new ImmutableJWKSet<>(jwkSet);
    }

    @Bean
    public JwtDecoder jwtDecoder(KeyPair keyPair) {
        return NimbusJwtDecoder.withPublicKey((RSAPublicKey) keyPair.getPublic()).build();
    }

    @Bean
    public ProviderSettings providerSettings() {
        return ProviderSettings.builder().issuer("http://localhost:9000").build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User.withUsername("zhangsan")
                .password("$2a$10$5nlwTDZ9LNbz7UMKcseY3u6YqbkGrMe2tQirfrwrCJAKrDAM78bUa")
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(userDetails);
    }

    public static void main(String[] args) {
        System.out.println(new BCryptPasswordEncoder().encode("123456"));
    }

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

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    KeyPair generateRsaKey() {
        KeyPair keyPair;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            keyPair = keyPairGenerator.generateKeyPair();
        } catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
        return keyPair;
    }
}

自定义登录页面

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
    <title>Spring Security OAuth 2.0 Sample</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}">
</head>
<body>
<div class="container">
    <h1>Login</h1>
    <p th:if="${loginError}" style="font-weight:bold;color:red;">Wrong username or password</p>
    <form th:action="@{/login}" method="post">
        <div class="form-row">
            <div class="form-group">
                <label for="username">Username</label>
                <input type="text" id="username" name="username" placeholder="&#x8BF7;&#x8F93;&#x5165;&#x7528;&#x6237;&#x540D;" autofocus="autofocus" class="form-control">
                <small class="form-text text-muted">user1 / password</small>
            </div>
        </div>
        <div class="form-row">
            <div class="form-group">
                <label for="password">Password</label>
                <input type="password" id="password" name="password" placeholder="&#x8BF7;&#x8F93;&#x5165;&#x5BC6;&#x7801;" class="form-control">
            </div>
        </div>
        <div class="form-row">
            <div class="form-group">
                <label for="verificationCode">&#x9A8C;&#x8BC1;&#x7801;</label>
                <input type="text" id="verificationCode" name="verificationCode" placeholder="&#x8BF7;&#x8F93;&#x5165;&#x9A8C;&#x8BC1;&#x7801;" class="form-control">
            </div>
        </div>
        <button type="submit" class="btn btn-primary">&#x767B;&#x5F55;</button>
    </form>
</div>
<script src="/webjars/jquery/jquery.min.js" th:src="@{/webjars/jquery/jquery.min.js}"></script>
<script src="/webjars/bootstrap/js/bootstrap.min.js" th:src="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>
</body>
</html>

默认的Controller


package com.cjs.example.oauth2.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * @Author ChengJianSheng
 * @Date 2022/4/2
 */
@Controller
public class DefaultController {

    @GetMapping("/")
    public String root() {
        return "redirect:/index";
    }

    @GetMapping("/index")
    public String index() {
        return "index";
    }

    @GetMapping("/login")
    public String login() {
        return "login";
    }

    @GetMapping("/login-error")
    public String loginError(Model model) {
        model.addAttribute("loginError", true);
        return login();
    }
}

获取token


GET http://localhost:9000/oauth2/authorize?response_type=code&client_id=client-2&redirect_uri=http://www.baidu.com

POST /oauth2/token HTTP/1.1
Host: localhost:9000
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=xxx&redirect_uri=http://www.baidu.com

OAuth2 Authorization Server

抓包

OAuth2 Authorization Server

OAuth2 Authorization Server

项目结构

OAuth2 Authorization Server

参考

https://github.com/jgrandja/spring-security-oauth-5-2-migrate/tree/main/client-app

Original: https://www.cnblogs.com/cjsblog/p/16093459.html
Author: 废物大师兄
Title: OAuth2 Authorization Server

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/612035/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Jenkins权限配置

    Jenkins权限配置 需要的插件 一、添加用户 二、修改配置 三、管理添加角色 添加全局查看角色 给全局角色添加用户(Anonymous-任何人) 添加角色(全局,项目) 检查项…

    数据库 2023年6月11日
    080
  • Hello Word

    编写代码 public class  hello{ public static void main(String[] args){ System.out.print("H…

    数据库 2023年6月11日
    056
  • MySQL 中如何定位 DDL 被阻塞的问题

    经常碰到开发、测试童鞋会问,线下开发、测试环境,执行了一个DDL,发现很久都没有执行完,是不是被阻塞了?要怎么解决? 包括在群里,也经常会碰到类似问题:DDL 被阻塞了,如何找到阻…

    数据库 2023年5月24日
    060
  • 解决Laravel报错No application encryption key has been specified的问题

    可能有些小伙伴从git上拉下来的项目在本地运行时会报No application encryption key has been specified的错,如图: 这是因为.env文…

    数据库 2023年6月14日
    074
  • NO.5 MySQL-笔记

    404. 抱歉,您访问的资源不存在。 可能是URL不正确,或者对应的内容已经被删除,或者处于隐私状态。 [En] It may be that the URL is incorre…

    数据库 2023年5月24日
    086
  • 部署前后端为独立的 Docker 节点

    在『服务器部署 Vue 和 Django 项目的全记录』一文中,介绍了在服务器中使用 Nginx 部署前后端项目的过程。然而,当 Web 应用流量增多时,需要考虑负载均衡、流量分发…

    数据库 2023年6月14日
    0100
  • Docker常用命令

    镜像:Docker 镜像是用于创建 Docker 容器的模板容器:容器是独立运行的一个或一组应用仓库:用来保存镜像,可以理解为代码控制中的代码仓库 一个仓库中包含多个镜像,以镜像为…

    数据库 2023年6月11日
    082
  • mysql配置环境变量

    安装MySQL 5.6,0.28 官网下载解压即可 下载后解压 然后以管理身份打开 my.ini配置(得先配置) bin>mysqld –initialize-i…

    数据库 2023年6月9日
    087
  • Mysql数据库体系

    Mysql数据库体系如下(手绘): 描述: 1.DBMS:database system management是数据库管理软件,平时我们使用的数据库的全称,是C/S架构(clien…

    数据库 2023年6月6日
    089
  • jenkins-配置python

    1. 进入”Dashboard”界面,点击左侧”构建执行状态” 2. 点击列表设置图标 3. 勾选”Environmen…

    数据库 2023年6月14日
    065
  • 23种设计模式之访问者模式(Visitor Pattern)

    文章目录 概述 访问者模式的优缺点 访问者模式的使用场景 访问者模式的结构和实现 * 模式结构 模式实现 总结 概述 访问者模式把数据结构和作用于结构上的操作解耦合,使得操作集合可…

    数据库 2023年6月6日
    087
  • 将博客园内博客移至CSDN

    访问csdn搬家链接:https://mp.csdn.net/mp_blog/tools/move 选择博客园,输入博客园地址、输入搬家通知地址👉勾选”我同意&#822…

    数据库 2023年6月14日
    0108
  • 001从零开始入门Entity Framework Core——基础知识

    1、对于 EF Core,使用模型执行数据访问。 模型由 实体类和表示数据库会话的 上下文对象构成。 上下文对象允许查询并保存数据。 2、EF 支持以下模型开发方法: 从现有数据库…

    数据库 2023年6月14日
    092
  • vim+vundle配置

    Linux环境下写代码虽然没有IDE,但通过给vim配置几个插件也足够好用。一般常用的插件主要包括几类,查找文件,查找符号的定义或者声明(函数,变量等)以及自动补全功能。一般流程都…

    数据库 2023年6月9日
    088
  • 工具 | 常用 MySQL 内核 Debug 技巧

    作者:柯煜昌 顾问软件工程师目前从事 RadonDB MySQL 容器化研发,华中科技大学研究生毕业,有多年的数据库内核开发经验。 掌握 MySQL 内核源码的阅读和调试能力,不仅…

    数据库 2023年5月24日
    0111
  • Linux远程终端连接工具:SecureCRT

    SecureCRT SecureCRT是一款支持 SSH2、SSH1、Telnet、Telnet/SSH、Relogin、Serial、TAPI、RAW 等协议的终端仿真程序 Se…

    数据库 2023年6月11日
    0104
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球