SpringCloud整合 Oauth2+Gateway+Jwt+Nacos 实现授权码模式的服务认证

2023-11-01

前言

目前正在出一个SpringCloud进阶系列教程,含源码解读, 篇幅会较多, 喜欢的话,给个关注❤️ ~

前段时间拖更了,主要事情比较多和杂,不多废话了,直接给大家开整吧~ 本节重点是给大家介绍Oauth2,将会带大家从0到1搭建一个 SpringCloud项目,以及整合Oauth2 Server,实现基本的授权和认证功能。

什么是OAuth2.0

跟往常一样,在学习新知识前,首先明白它是啥?

我相信大部分人应该都对它都有了解,甚至使用过。如果你没有亲自搭建过这样的服务,但是在对接第三方平台的时候,首先需要去进行服务认证,在大多数的认证服务中,很多都是基于OAuth2.0的。

首先我们需要明白这样一个概念, OAuth ,它是一个授权(Authorization)的开放标准,大家都可以基于这个标准去实现。OAuth基于HTTPS,以及APIs,Service应用使用```access_token````来进行身份验证。

它主要有OAuth 1.0OAuth 2.0,二者不兼容。OAuth2.0 是目前广泛使用的版本,大部分说的OAuth都是OAuth2.0

基本流程

它的基本流程是怎么样的呢?我们先看张图

上图中所涉及到的对象分别为:

  • Client 第三方应用,我们的应用就是一个Client
  • Resource Owner 资源所有者,即用户
  • Authorization Server 授权服务器,即提供第三方登录服务的服务器,如Github
  • Resource Server 拥有资源信息的服务器,通常和授权服务器属于同一应用

根据上图的信息,我们可以知道OAuth2的基本流程为:

  1. 第三方应用请求用户授权。
  2. 用户同意授权,并返回一个凭证(code)
  3. 第三方应用通过第二步的凭证(code)向授权服务器请求授权
  4. 授权服务器验证凭证(code)通过后,同意授权,并返回一个资源访问的凭证(Access Token)
  5. 第三方应用通过第四步的凭证(Access Token)向资源服务器请求相关资源。
  6. 资源服务器验证凭证(Access Token)通过后,将第三方应用请求的资源返回。

基本概念就介绍到这里,下面我们就一起整一下吧~

基本项目框架搭建

首先我们的准备工作,需要安装好Nacos,如果还不会的同学可以参考我之前写的文章,这里默认大家都安装好了。本节呢,我们统一给大家尝试高版本,我这里的SpringBoot版本是2.6+,SpringCloud2021,基本上都是比较新的版本了

下面新建一个maven项目,父工程的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>pkq-cloud</groupId>
    <artifactId>com.pkq.cloud</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>spring-cloud-oauth2-server</module>
    </modules>

    <packaging>pom</packaging>

    <properties>
        <fate.project.version>1.0.0</fate.project.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <maven.plugin.version>3.8.1</maven.plugin.version>
        <spring.boot.version>2.6.3</spring.boot.version>
        <spring-cloud.version>2021.0.1</spring-cloud.version>
        <spring-cloud-alibaba.version>2021.0.1.0</spring-cloud-alibaba.version>
        <alibaba.nacos.version>1.4.2</alibaba.nacos.version>
        <alibaba.sentinel.version>1.8.3</alibaba.sentinel.version>
        <alibaba.dubbo.version>2.7.15</alibaba.dubbo.version>
        <alibaba.rocketMq.version>4.9.2</alibaba.rocketMq.version>
        <alibaba.seata.version>1.4.2</alibaba.seata.version>
        <mybatis.plus.version>3.5.1</mybatis.plus.version>
        <knife4j.version>3.0.2</knife4j.version>
        <swagger.version>3.0.0</swagger.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- springBoot -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- springCloud -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

复制代码

大家可以直接复制过去,下面我们新建一个模块叫spring-cloud-oauth2-server,这个模块就是OAuth2的授权服务中心,看下它的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>com.pkq.cloud</artifactId>
        <groupId>pkq-cloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-cloud-oauth2-server</artifactId>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--SpringBoot2.4.x之后默认不加载bootstrap.yml文件,需要在pom里加上依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>

        <!-- Nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.alibaba.nacos</groupId>
                    <artifactId>nacos-client</artifactId>
                </exclusion>
            </exclusions>
            <version>${spring-cloud-alibaba.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.alibaba.nacos</groupId>
                    <artifactId>nacos-client</artifactId>
                </exclusion>
            </exclusions>
            <version>${spring-cloud-alibaba.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
            <version>${alibaba.nacos.version}</version>
        </dependency>

        <!-- Druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.22</version>
        </dependency>
        <!-- MySQL -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>
        <!-- MyBatis-Plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis.plus.version}</version>
        </dependency>
        <!-- redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.nimbusds</groupId>
            <artifactId>nimbus-jose-jwt</artifactId>
            <version>9.9.3</version>
        </dependency>
        <!--security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.0</version>
        </dependency>
        <!--thymeleaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

    </dependencies>


</project>
复制代码

该模块下的配置文件bootstrap.yml,不知道啥是bootstrap配置文件的可以参考我之前的文章,在讲Nacos的时候有给大家讲

server:
  port: 10001

spring:
  application:
    name: oauth2-server
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: ********
    driver-class-name: com.mysql.cj.jdbc.Driver
  redis:
    host: 127.0.0.1
    port: 6379
    database: 2

  cloud:
    nacos:
      discovery:
        #注册中心地址
        server-addr: 127.0.0.1:8848
      config:
        file-extension: yaml
        #配置中心地址
        server-addr: 127.0.0.1:8848

  thymeleaf:
    cache: false


mybatis-plus:
  mapper-locations: classpath:mapper/*/*.xml,mapper/*.xml
  global-config:
    db-config:
      id-type: auto
      field-strategy: NOT_EMPTY
      db-type: MYSQL
  configuration:
    map-underscore-to-camel-case: true
    call-setters-on-nulls: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl


复制代码

这里为了方便演示,直接把配置写到文件了,也可以放到Nacos配置中心里,这里主要是当服务注册中心使用, Nacos用到的sql我放到项目的db目录下了,需要的可自行获取

下面我们加个启动类,看是否启动正常~

/**
 * @Author 
 * @Description 启动类
 * @Date
 */
// @SpringCloudApplication 被弃用
@SpringBootApplication
public class OauthServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(OauthServerApplication.class, args);
    }
}
复制代码

在新版本中@SpringCloudApplication被弃用了,我们直接使用@SpringBootApplication就好了,你会发现,啥都不加服务也会自动注册,说明新版本中已经比较完善了。@SpringCloudApplication本质上也就是在@SpringBootApplicaiton中加了几个注解

@Deprecated
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient
public @interface SpringCloudApplication {
}

复制代码

启动一下,如果正常说明第一步我们就成功了~ 下面我们进入正题

搭建 OAuth2 Server

首先我们要去搭建一个认证服务配置,Oauth2有多种模式,常见的有密码模式还有授权码模式,现在比较流行的就是授权码模式,下面要带大家实现的就是基于授权码模式

认证服务配置

/**
 * @Author qcl
 * @Description Oauth2 认证服务配置
 * @Date
 */
@AllArgsConstructor
@Configuration
@EnableAuthorizationServer
public class Oauth2ServerConfig extends AuthorizationServerConfigurerAdapter {

    // 认证管理器
    @Autowired
    private AuthenticationManager authenticationManager;

    // 数据源
    @Autowired
    private DataSource dataSource;

    //密码加密方式
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // 自定义身份认证
    @Autowired
    private UserServiceImpl userDetailsService;

    @Bean
    public ClientDetailsService jdbcClientDetailsService(){
        //存储client信息
        return new JdbcClientDetailsService(dataSource);
    }

    @Bean
    public TokenStore tokenStore(){
        // token存储
        return new JdbcTokenStore(dataSource);
    }

    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        // 授权码模式
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //配置 client信息从数据库中取
        clients.withClientDetails(jdbcClientDetailsService());
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenStore(tokenStore())//token存储方式
                .authenticationManager(authenticationManager)// 开启密码验证,由 WebSecurityConfigurerAdapter
                .userDetailsService(userDetailsService)// 读取验证用户信息
                .authorizationCodeServices(authorizationCodeServices())
                .setClientDetailsService(jdbcClientDetailsService());
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //  配置Endpoint,允许请求
        security.tokenKeyAccess("permitAll()") // 开启/oauth/token_key 验证端口-无权限
                .checkTokenAccess("isAuthenticated()") // 开启/oauth/check_token 验证端口-需权限
                .allowFormAuthenticationForClients()// 允许表单认证
                .passwordEncoder(passwordEncoder());   // 配置BCrypt加密
    }


}

复制代码

这里我们使用基于持久化的配置,相关注释已经加上了,那么它的数据存在哪呢?它是存在数据库中,下面我们需要准备几张表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for oauth_access_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token`  (
  `token_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `token` blob NULL,
  `authentication_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `user_name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `client_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `authentication` blob NULL,
  `refresh_token` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '存储生成的access_token' ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of oauth_access_token
-- ----------------------------

-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details`  (
  `client_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
  `resource_ids` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `client_secret` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `scope` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `authorized_grant_types` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `web_server_redirect_uri` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `authorities` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `access_token_validity` int NULL DEFAULT NULL,
  `refresh_token_validity` int NULL DEFAULT NULL,
  `additional_information` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL,
  `autoapprove` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'false',
  PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '存储客户端的配置信息' ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
INSERT INTO `oauth_client_details` VALUES ('clientId1', '1', '$2a$10$WFDhpNunaH0aPlPQp33q8OAlqot6NANran7HeP/DSSWt1kVRvF2d2', 'all', 'password,refresh_token,authorization_code,implicit,client_credentials', 'https://www.baidu.com', 'ROLE_ADMIN', 604800, 1209600, '{\"info\": \"test\"}', 'false');

-- ----------------------------
-- Table structure for oauth_code
-- ----------------------------
DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code`  (
  `code` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `authentication` blob NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '存储服务端系统生成的code的值' ROW_FORMAT = DYNAMIC;


-- ----------------------------
-- Table structure for oauth_refresh_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token`  (
  `token_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `token` blob NULL,
  `authentication` blob NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '存储刷新令牌的refresh_token' ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of oauth_refresh_token
-- ----------------------------

SET FOREIGN_KEY_CHECKS = 1;


复制代码

默认给大家已经插入了一条客户端的配置,后边我们会用到它

在代码中,我们可以看到导入了一个UserDetailsService,这个类需要我们实现它

/**
 * @Author qcl
 * @Description 用户信息
 * @Date
 */
@Slf4j
@Service
public class UserServiceImpl implements UserDetailsService {

    @Autowired
    private SysUserService sysUserService;

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        UserInfo userInfo = sysUserService.getUserByUserName(userName);
        log.warn("user: " + userInfo.getUsername());
        if (userInfo == null){
            throw new UsernameNotFoundException("用户不存在: " + userName);
        }

        List<GrantedAuthority> authorities=new ArrayList<>();
        //获取用户权限
        List<String> permissions = userInfo.getPermissions();
        permissions.forEach(permission->{
            authorities.add(new SimpleGrantedAuthority("ROLE_" + permission));
        });

        // 这里一定要基于 BCrypt 加密,不然会不通过
        UserDetails user = new User(userInfo.getUsername(), new BCryptPasswordEncoder().encode(userInfo.getPassword()), authorities);
        log.warn(user.toString());
        return user;
    }
}

复制代码

它主要是用来验证和存储用户信息的,场景就是用户在进行登录授权的时候会走到这里边,我们可以在这进行用户身份校验,SysUserService也是我们自己实现的,用来获取用户信息,这里为了方便演示,直接模拟了

@Service
public class SysUserServiceImpl implements SysUserService {

    @Override
    public UserInfo getUserByUserName(String userName) {
        ArrayList<String> list = new ArrayList<>();
        list.add("ADMIN");
        UserInfo user = new UserInfo();
        user.setUserId("1111");
        user.setPassword("123321");
        user.setUsername(userName);
        user.setPermissions(list);
        return user;
    }
}

复制代码

这里给大家提示下可能遇到的坑, 就是userInfo.getPassword(), 如果你是从数据库读的,如果使用了BCryptPasswordEncoder加密过的,那么可以,如果不是的话,你就需要BCryptPasswordEncoder加密一下,不过这样不大安全,推荐是大家在数据库加密,我这里为了方便给大家演示

开启Spring Security配置

下面我们整一下安全配置

/**
 * @Author 
 * @Description 开启Spring Security配置
 * @Date
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/oauth/check_token");
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
                .antMatchers("/view/**").permitAll()
                .anyRequest().authenticated();
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

复制代码

认证流程

现在我们就可以启动它了,然后到浏览器访问http://localhost:10001/oauth/authorize?client_id=clientId1&response_type=code&scope=all&redirect_uri=https://www.baidu.com

有几个参数,给大家解释一下:

  • /oauth/authorize 这个是Ouath2默认开启的规范接口,通过它进行客户端验证
  • client_id 客户端ID, 也就是分配给第三方的认证ID,
  • response_type=code 返回的类型code,也就是我们开启的是授权码模式
  • scope 授权范围, 需要和我们数据库配置的一致
  • redirect_uri 认证成功后重定向的地址,它会携带code 参数

说了这些,一开始接触的朋友,可能还是不大清除,下面给一张图给大家体会一下:

如果有朋友接过h5微信的登录,相必会深有体会

下面,我们就去访问它。当我们在浏览器输入地址的时候会自动跳转到/login的地址页面。这个页面是框架自带的,有时候,我们有很多个性化的需求需要自定义,下面就带大家整一下自定义的登录和授权页面

自定义授权和登录页

配置也很简单,我们只需要修改WebSecurityConfig的配置

 @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
                .antMatchers("/view/**", "/user/**").permitAll()
                .anyRequest().authenticated();

        http.formLogin()
                // 自定义处理登录逻辑的地址login
                .loginProcessingUrl("/login")
                // 自定义登录页面
                .loginPage("/view/login")
                .permitAll()
                .and()
                .csrf().disable().httpBasic();
    }
复制代码

这里,我已经准备好了模板,我们使用thymeleaf简单的给大家演示一下

/**
 * @Author 
 * @Description
 * @Date
 */
@Controller
@RequestMapping("/view")
public class ViewController {

    @RequestMapping("/login")
    public String loginView(Model model) {
        return "oauth-login";
    }
}
复制代码
  • templates/oauth-login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www/thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>

<div>
    <h1>授权登录页</h1>
    <br/>

    <form name="loginForm" method="post" action="/login">
        用户名: <input type="text" name="username" placeholder="用户名" />

        <br/>
        密码: <input type="password" name="password" placeholder="密码" />

        <button type="submit">点击登录</button>
    </form>


</div>
</body>
</html>

复制代码

因为我们引入的是```spring-boot-starter-thymeleaf````,所以默认给我们配置好了,直接添加html就可以了,下面我们再自定义一下授权页,授权页就是比如你用qq登录了网站, 你扫码成功了或者密码输入整个了,这时候会提示获取你的信息,你同意了才可以继续操作

自定义授权页,相对复杂一点,如果对流程和源码不咋熟的话,有点困难

首先需要我们改一下Oauth2ServerConfig的配置,因为授权在这里

   @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenStore(tokenStore())//token存储方式
                .authenticationManager(authenticationManager)// 开启密码验证,由 WebSecurityConfigurerAdapter
                .userDetailsService(userDetailsService)// 读取验证用户信息
                .authorizationCodeServices(authorizationCodeServices())
                // 自定义授权跳转
                .pathMapping("/oauth/confirm_access", "/custom/confirm_access")
                // 自定义异常跳转
                .pathMapping("/oauth/error", "/view/oauth/error")
                .setClientDetailsService(jdbcClientDetailsService());
    }
复制代码

默认下是"/oauth/confirm_access,我们替换成自定义的/custom/confirm_access,同样的自定义异常的页面也是可以的。下一步,我们需要加个控制器AuthorizeController

@SessionAttributes("authorizationRequest")
@Controller
public class AuthorizeController {

    @RequestMapping("/custom/confirm_access")
    public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {
        AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get("authorizationRequest");
        ModelAndView view = new ModelAndView();
        view.setViewName("oauth-authorize");
        view.addObject("clientId", authorizationRequest.getClientId());
        view.addObject("scopes",authorizationRequest.getScope());
        return view;
    }

}
复制代码

紧接着就是模板文件 oauth-authorize.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www/thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>授权页</title>
</head>
<body>

<div>
    <h3 th:text="${clientId}+' 请求授权,该应用将获取你的以下信息'"></h3>
    <p>昵称,头像和性别</p>
    授权后表明你已同意 <a  href="#boot" style="color: #E9686B">服务协议</a>
    <form method="post" action="/oauth/authorize">
        <input type="hidden" name="user_oauth_approval" value="true">
        <div th:each="item:${scopes}">
            <input type="radio" th:name="'scope.'+${item}" value="true" checked>同意
            <input type="radio" th:name="'scope.'+${item}" value="false" >拒绝
        </div>
        <input name="authorize" value="同意/授权" type="submit">
    </form>


</div>
</body>
</html>
复制代码

这一步,你可以向用户展示信息,这里就给大家简单写写,我们主要体验功能, 好,有了这些准备之后,我们可以正式进入测试环节了

测试环节

首先访问http://localhost:10001/oauth/authorize?client_id=clientId1&response_type=code&scope=all&redirect_uri=https://www.baidu.com,然后自动跳转到/view/login,输入完用户名和密码会跳转到授权页

也就是/custom/confirm_access

点击同意之后会跳转到https://www.baidu.com?code=xxx,会携带code参数,下面我们就可以通过code获取access_token了,获取token````的地址,http://localhost:10001/oauth/token```,使用```POST```请求,因为我们开启了``` .allowFormAuthenticationForClients()// 允许表单认证```,所以可以进行表单请求,如果没开启的话,是没法的。

我们需要拿着secret去请求,也就是数据库存储的client_secret,这个一般由服务提供者提供,也就是所谓的秘钥

成功之后会返回```access_token``,到这里我们就算搭建成功了

说一下这里大家可能遇到的坑,如果你遇到了not like BCrypt 的报错,然后明明你的代码配置都对,可怎么改都没有用,八成是你的secret不对,首先排查你传的secret,这个时没有BCrypt过的,其次就是数据库存储的secret 这里不能明文存储,需要是加密过的,这个也很简单,我们本地加密一下存进去就行了

public class BCryptTest {
    public static void main(String[] args) {
        System.out.println(new BCryptPasswordEncoder().encode("123321"));
    }
}
复制代码

结束语

本节到这里就结束啦,希望可以对大家有所帮助。

​​​​​​​ 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpringCloud整合 Oauth2+Gateway+Jwt+Nacos 实现授权码模式的服务认证 的相关文章

随机推荐

  • 前端常用的一些正则表达式

    在线测试正则的网址 https regexr com 1 正整数 1 9 D 正整数不仅需要判断当前输入的全是数字 还需要保证第一位数字不是0 具体使用方式 直接在方法中写的话
  • Web前端学习(HTML)学习---下(表格标签,列表标签,表单标签)案例

    作者 旧梦拾遗186 专栏 C语言编程 小比特成长日记 前言 趁年轻 余额不足可以挣 电量不足可以充 时间匆匆不再回来 趁年轻就去多付出 不攀比 不抱怨 不计较 多付出 因为有一种努力叫靠自己 前一篇文章我们着重学习了HTML中的 标题标签
  • CSS--滑动门和过渡效果

    滑动门 滑动门出现的背景 制作网页时 为了美观 常常需要为网页元素设置特殊形状的背景 比如微信导航栏 有凸起和凹下去的感觉 最大的问题是里面的字数不一样多 咋办 为了使各种特殊形状的背景能够自适应元素中文本内容的多少 出现了CSS滑动门技术
  • Blender常用快捷键整理

    物体操作快捷键 即选中物体 G键 移动物体 R键 旋转物体 S键 缩放物体 移动 旋转或缩放物体时 按下X Y或Z键 按X Y或Z轴方向移动 旋转或缩放 TAB键 切换为编辑模式 CTRL A 弹出应用菜单 物体模式旋转缩放后应用旋转与缩放
  • 人工智能在通信领域的应用

    人工智能的出现使得各个行业都有了新的发展方向 通过和人工智能结合 使得自己的行业打破传统的方式 以一种新的姿态进入人们的视线中 现在我们都离不开通信技术 很多人对于人工智能给通信领域带来什么的改变 在这篇文章中我们会详细的介绍这一问题 大家
  • 基于opencv的车道线识别 方法二(极易实现(python))

    基于opencv的车道线识别 方法二 效果图 语言 平台 所需的库 步骤及原理 1 导入库 2 二值化 3 提取感兴趣区域 4 剔除噪点 5 找出值不为零的点 即车道线 并将其绘制在原图上 完整代码 效果图 语言 python 平台 pyc
  • 关于FFmpeg里的GPL和LGPL协议

    参考博文 谢谢博主的分享 http www cnblogs com findumars p 3556883 html GPL介绍 我们很熟悉的Linux就是采用了GPL GPL协议和BSD Apache Licence等鼓励代码重用的许可很
  • python并发编程学习笔记--单线程,多线程,多进程 day06

    Python并发编程是指同时处理多个任务的技术 包括单线程 多线程和多进程三种方式 1 单线程 单线程是指在一个进程中只有一个线程在执行任务的情况 虽然只有一个线程在执行任务 但可以使用异步编程模型来实现并发操作 从而达到提高程序效率的目的
  • 置信度传播算法(Belief Propagation)

    基础知识 条件概率 Conditional Probability 相互独立时 p A B p A 贝叶斯规则 贝叶斯网络 Bayesian Network 定了一个独立的结构 一个节点的概率仅依赖于它的父节点 贝叶斯网络适用于稀疏模型 即
  • 【Angular】——无限级下拉列表框

    前言 前段时间换了新框架 将前后端分离 对Angular2有点感兴趣 所以参与一起封装组件 在小5的帮助下 学会了 好多东西 这里总结下封装的无限级下拉列表框 dropdownlist ts import Component OnInit
  • IDEA 调试小技巧

    条件断点 循环中经常用到这个技巧 比如 遍历中 想让断点停在某个特定值 见上图 在断点位置 右击会弹出一个界面 在condition中填写断点条件 在调试的时候 断点会自动在断点条件 i 6 为 true时候停下 跳过为false的条件 回
  • Java对象与byte[]数组之间的相互转化,压缩解压缩操作

    原文 http blog csdn net NsdnResponsibility article details 51028739 comments 下面介绍一下java对象之间和byte 数组之间的相互转化 并对byte 数据进行压缩操作
  • 数学的回忆(零)——傅立叶

    一 什么是频域 从我们出生 我们看到的世界都以时间贯穿 股票的走势 人的身高 汽车的轨迹都会随着时间发生改变 这种以时间作为参照来观察动态世界的方法我们称其为时域分析 而我们也想当然的认为 世间万物都在随着时间不停的改变 并且永远不会静止下
  • 如何使用KubeSphere3.0的DevOps系统构建dotnet core应用

    如何使用KubeSphere3 0的DevOps系统构建dotnet core应用 因KubeSphere的DevOps系统官方未提供 net core的ci cd解决方案 需要自己进行DIY 现把实施过程记录下来 供需要的小伙伴自取 前提
  • FISCO BCOS 六、通过Caliper进行压力测试程序(及常见问题)

    目录 1 环境要求 第一步 配置基本环境 这里我使用的是Ubuntu20 04 第二步 安装NodeJS 第三步 部署Docker 第四步 安装Docker Compose 2 Caliper部署 第一步 部署 第二步 绑定 第三步 快速体
  • Jedis使用

    Jedis Jedis是Redis官方推荐的Java连接服务工具 Java语言连接redis服务还有这些SpringData Redis Lettuce 下载地址 https mvnrepository com artifact redis
  • MySQL left join优化

    问题描述 遇到了一个需要4个表连接查询的问题 数据量不是很大 两个表大概9000条数据 另外两个表大概几百条数据 但是每次查询时间都需要50秒左右的时间 SELECT FROM gzgdm gz gd region region LEFT
  • 类组件使用mobx实现数据修改以及请求数据

    src下创建新的共享数据文件test js import observable computed action autorun runInAction configure makeAutoObservable from mobx impor
  • 处理高并发的方法

    处理高并发 六种方法 1 系统拆分 将一个系统拆分为多个子系统 用dubbo来搞 然后每个系统连一个数据库 这样本来就一个库 现在多个数据库 这样就可以抗高并发 2 缓存 大部分的高并发场景 都是读多写少 那你完全可以在数据库和缓存里都写一
  • SpringCloud整合 Oauth2+Gateway+Jwt+Nacos 实现授权码模式的服务认证

    前言 目前正在出一个SpringCloud进阶系列教程 含源码解读 篇幅会较多 喜欢的话 给个关注 前段时间拖更了 主要事情比较多和杂 不多废话了 直接给大家开整吧 本节重点是给大家介绍Oauth2 将会带大家从0到1搭建一个 Spring