IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 微服务之间的单点登录实现(Gateway技术、Jwt创建令牌) -> 正文阅读

[JavaScript知识库]微服务之间的单点登录实现(Gateway技术、Jwt创建令牌)

需求分析

传统的登录系统中,每个站点都实现了自己专用的登录模块。但是各站点的登录状态相互不认可,访问各站点还需要逐一进行手工登录,大大降低了用户的体验感。

在这里插入图片描述

?如上图所示,用户每次访问一个系统都需要再次进行授权认证,这样就显得相对繁琐,并且系统代码的重复也会比较高,我们将这样的系统称为多点登录系统。为了解决这一现象,单点登录系统就出现了。

单点登录系统

单点登录,英文是?Single Sign On(缩写为 SSO)。即多个站点共用一台认证授权服务器,这样用户在任何一个站点登录后,就可以避免再次登录才能访问其它的站点。并且各站点间可以通过该登录状态直接交互。

在这里插入图片描述

准备工作

项目结构

?

修改网关配置文件

在原网关配置中添加如下配置:

        - id: router02
          uri: lb://sca-auth
          predicates:
            #- Path=/auth/login/** #刚开始时这里必须是login,因为是默认的,后续可以更改
            - Path=/auth/oauth/** #微服务架构(更改之后),需要令牌
          filters:
            - StripPrefix=1

现网关配置文件内容:

server:
  port: 9000
spring:
  application:
    name: sca-resource-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848
        file-extension: yml
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: router01
          uri: lb://sca-resource
          predicates:
            - Path=/sca/resource/upload/**
          filters:
            - StripPrefix=1
        - id: router02
          uri: lb://sca-auth
          predicates:
            #- Path=/auth/login/** #刚开始时这里必须是login,因为是默认的,后续可以更改
            - Path=/auth/oauth/** #微服务架构(更改之后),需要令牌
          filters:
            - StripPrefix=1
      globalcors: #跨域配置
        corsConfigurations:
          '[/**]':
            allowedOrigins: "*"
            allowedHeaders: "*"
            allowedMethods: "*"
            allowCredentials: true
    sentinel:
      transport:
        dashboard: localhost:8180
        port: 8719
      eager: true

添加相关依赖

在权限认证项目pom文件中添加如下依赖:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

现 pom文件内容:

<?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>02-sca-files</artifactId>
        <groupId>com.jt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>sca-auth</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

</project>

构建权限认证项目配置文件

在sca-auth工程中创建bootstrap.yml文件,内容如下:

server:
  port: 8071
spring:
  application:
    name: sca-auth #定义nacos服务名称
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848

前端UI设计

前端UI设计工程项目结构:

之所以有两个登录界面,是因为login.html是用于postman测试的,而login-sso.html实现了可以直接在浏览器直接输入地址访问的功能.用户登录之后,可以进入到文件上传界面.

各自的HTML内容如下:

1)fileupload.html文件内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件上载演示</title>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <style>
        ul>li{
            list-style-type: none;
        }
    </style>
</head>
<body>
<h1>文件上传案例演示:</h1>
<form id="fileForm" method="post"
                    enctype="multipart/form-data"
                    onsubmit="return doUpload()">
    <div>
        <ul>
            <li><input id="uploadFile" type="file" name="uploadFile"></li>
            <li><button type="submit">上传文件</button></li>
        </ul>
    </div>
</form>
</body>
<script>
    //jquery代码的表单提交事件
    function doUpload(){
        debugger //debug窗口打开以后调试代码
        //获得用户选中的所有图片(获得数组)
        let files=document.getElementById("uploadFile").files;
        if(files.length>0){
            //获得用户选中的唯一文件(从数组中取出)
            let file=files[0];
            //开始上传这个文件
            //由于上传代码比较多,不想和这里其它代码干扰,所以定义一个方法调用
            upload(file);
        }
        //阻止表单提交效果
        return false;
    };
    // 将file上传到服务器的方法
    function upload(file){
        //定义一个表单
        let form=new FormData();
        //将图片添加到表单中
        form.append("uploadFile",file);
        let url="http://localhost:9000/sca/resource/upload/";
        //异步提交方式1
        axios.post(url,form)
             .then(function (response){
                 alert("upload ok")
                 console.log(response.data);
             })
             .catch(function (e){//失败时执行catch代码块
                 console.log(e);
             })
        //异步提交方式2
        // axios({
        //     url:"http://localhost:8881/resource/upload/",
        //     method:"post",
        //     data:form
        // }).then(function(response){
        //     alert("upload ok")
        //     console.log(response.data);
        // })
    }
</script>
</html>

2)login.html文件内容:

<!doctype html>
<html lang="en">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <title>login</title>
</head>
<body>
<div class="container"id="app">
    <h3>Please Login</h3>
    <form>
        <div class="mb-3">
            <label for="usernameId" class="form-label">Username</label>
            <input type="text" v-model="username" class="form-control" id="usernameId" aria-describedby="emailHelp">
        </div>
        <div class="mb-3">
            <label for="passwordId" class="form-label">Password</label>
            <input type="password" v-model="password" class="form-control" id="passwordId">
        </div>
        <button type="button" @click="doLogin()" class="btn btn-primary">Submit</button>
    </form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
    var vm=new Vue({
        el:"#app",//定义监控点,vue底层会基于此监控点在内存中构建dom树
        data:{ //此对象中定义页面上要操作的数据
            username:"",
            password:""
        },
        methods: {//此位置定义所有业务事件处理函数
            doLogin() {
                //1.定义url
                let url = "http://localhost:9000/auth/login"
                //2.定义参数

                let params = new URLSearchParams()
                params.append('username',this.username);
                params.append('password',this.password);
                //3.发送异步请求
                axios.post(url, params).then((response) => {
                    debugger
                    let result=response.data;
                    console.log(result);
                    if (result.state == 200) {
                        alert("login ok");
                        location.href="/fileupload.html"
                    } else {
                        alert(result.message);
                    }
                })
            }
        }
    });
</script>
</body>
</html>

3)login-sso.html文件内容:

<!doctype html>
<html lang="en">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <title>login</title>
</head>
<body>
<div class="container"id="app">
    <h3>Please Login</h3>
    <form>
        <div class="mb-3">
            <label for="usernameId" class="form-label">Username</label>
            <input type="text" v-model="username" class="form-control" id="usernameId" aria-describedby="emailHelp">
        </div>
        <div class="mb-3">
            <label for="passwordId" class="form-label">Password</label>
            <input type="password" v-model="password" class="form-control" id="passwordId">
        </div>
        <button type="button" @click="doLogin()" class="btn btn-primary">Submit</button>
    </form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
    var vm=new Vue({
        el:"#app",//定义监控点,vue底层会基于此监控点在内存中构建dom树
        data:{ //此对象中定义页面上要操作的数据
            username:"",
            password:""
        },
        methods: {//此位置定义所有业务事件处理函数
            doLogin() {
                //1.定义url
                //let url = "http://localhost:9000/auth/login"
                let url = "http://localhost:9000/auth/oauth/token"

                //2.定义参数

                let params = new URLSearchParams()
                params.append('username',this.username);
                params.append('password',this.password);
                params.append('client_id','gateway-client');
                params.append('grant_type','password');
                params.append('client_secret',"123456");
                // debugger
                //3.发送异步请求
                axios.post(url, params).then((response) => {
                    debugger
                    console.log(response.data);
                    let result = response.data;
                    localStorage.setItem("accessToken",result.access_token);
                    location.href="/fileupload.html"
                })
            }
        }
    });
</script>
</body>
</html>

注意区分两个登录文件的区别. login-sso.html主要是已经实现了参数的传递.

代码实现

权限认证工程项目结构

配置类内容

TokenConfig类

创建jwt令牌配置类,基于这个类实现令牌的创建和解析

package com.jt.auth.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
/**
 * 创建jwt令牌配置类,基于这个类实现令牌的创建和解析
 * jwt令牌由3个部分构成
 * 1)HEADER(头部信息:令牌信息)
 * 2)PAYLOAD(数据信息-用户信息,权限信息)
 * 3)SIGNATURE(签名信息-对header和payload部分进行加密)*/
@Configuration
public class TokenConfig {
    //定义令牌签发口令:当客户端在执行登录时,假如有携带这个信息,认证服务器就可以给他签发一个令牌
    //在对header和payload部分进行签名时,需要的一个口令
    private String SIGNING_KEY = "auth";
    //构建令牌生成器对象(构建和存储令牌)
    @Bean
    public TokenStore tokenStore(){
        return new JwtTokenStore(jwtAccessTokenConverter());
    }
    //jwt转换器,将任何数据转换为jwt字符串令牌
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        //设置加密/解密口令
        converter.setSigningKey(SIGNING_KEY);
        return converter;
    }
}

SecurityConfig类

定义登录的规则(成功登录时的返回信息与登录失败时的返回信息),创建认证管理器对象

package com.jt.auth.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //密码加密对象
    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    //在这个方法中定义登录规则
    //1.对所有请求放行(当前工程只做认证)
    //2.登陆成功信息的返回
    //3.登录失败信息的返回
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //关闭跨域攻击
        http.csrf().disable();
        //放行所有请求
        http.authorizeRequests().anyRequest().permitAll();
        //登录成功与失败的返回
        http.formLogin()
                .successHandler(successHandler())
                .failureHandler(failureHandler());
    }
    @Bean
    public AuthenticationSuccessHandler successHandler(){
        return (request, response, authentication)->{
            //1.构建map对象,封装响应数据
            Map<String,Object> map = new HashMap<>();
            map.put("state", 200);
            map.put("message", "login ok");
            //2.将对象转换为JSON,并写到客户端
            writeJsonToClient(response, map);
        };
    }
    @Bean
    public AuthenticationFailureHandler failureHandler(){
        return (request, response, authentication) -> {
            //1.构建map对象,封装响应数据
            Map<String,Object> map = new HashMap<>();
            map.put("state", 500);
            map.put("message", "login failure");
            //2.将对象转换为JSON,并写到客户端
            writeJsonToClient(response, map);
        };
    }
    private void writeJsonToClient(HttpServletResponse response,Object object) throws IOException {
        //2.将对象转换为JSON
        //Gson-->toJson (需要自己找依赖)
        //fastjson-->JSON (spring-cloud-starter-alibaba-sentinel)
        //jackson-->writeValueAsString (spring-boot-starter-web)
        String jsonStr = new ObjectMapper().writeValueAsString(object);
        //3.将json字符串写到客户端
        PrintWriter writer = response.getWriter();
        writer.println(jsonStr);
        writer.flush();
    }
    //创建认证管理器对象(此对象主要负责对客户端输入的用户信息进行认证),后面授权服务器会用到
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

UserDetailsServiceImpl类

登录时用户信息的获取和封装会在此对象进行实现.

package com.jt.auth.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * 登录时用户信息的获取和封装会在此对象进行实现,
 * 在页面点击登录按钮时会调用这个对象的loadUserByUsername方法,
 * 页面上输入的用户名会传给这个方法的参数
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private PasswordEncoder passwordEncoder;
    //UserDetails用户封装用户信息(认证和权限信息)
    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        //1.基于用户名查询用户信息(用户名,用户密码,用户状态...)
        //Userinfo userinfo = userMapper.selectUserByUsername(username);
        String encodePassword = passwordEncoder.encode("123456");
        //2.查询用户权限信息,这里给的是假数据
        List<GrantedAuthority> authorities =
                AuthorityUtils.createAuthorityList(//这里的权限信息先这么写,后面再讲其它
                        "sys:res:create", "sys:res:retrive");
        //3.对用户信息进行封装
        return new User(username, encodePassword, authorities);
    }
}

Oauth2Config类

完成所有配置的组装,在这个配置类中完成认证授权,JWT令牌签发等配置操作.

package com.jt.auth.config;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
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.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import java.util.Arrays;
/**完成所有配置的组装,在这个配置类中完成认证授权,JWT令牌签发等配置操作*/
@AllArgsConstructor
@EnableAuthorizationServer//开启认证和授权服务
@Configuration
public class Oauth2Config extends AuthorizationServerConfigurerAdapter {
    //此对象负责完成认证管理
    private AuthenticationManager authenticationManager;
    //负责完成令牌的创建,信息读取等
    private TokenStore tokenStore;
    //负责获取用户的详情信息(username,password,client_id,grand_type,client_secret)
    private ClientDetailsService clientDetailsService;
    //jwt令牌转换器(基于用户信息构建令牌和解析令牌)
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    //密码加密匹配器对象
    private PasswordEncoder passwordEncoder;
    //负责获取用户详细信息
    private UserDetailsService userDetailsService;
    /***/
    //设置认证端点的配置(/oauth/token)
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                //配置认证管理器
                .authenticationManager(authenticationManager)
                //验证用户的方法获得用户详情
                .userDetailsService(userDetailsService)
                //要求提交认证使用post请求方式,提高安全性
                .allowedTokenEndpointRequestMethods(HttpMethod.POST,HttpMethod.GET)
                //要配置令牌的生成,由于令牌生成比较复杂,下面有方法实现
                .tokenServices(tokenService());//这个被注释的话,默认令牌为uuid的
    }
    //定义令牌生成策略
    @Bean
    public AuthorizationServerTokenServices tokenService(){
        //这个方法的目标就是获得一个令牌生成器
        DefaultTokenServices services=new DefaultTokenServices();
        //支持令牌刷新策略(令牌有过期时间)
        services.setSupportRefreshToken(true);
        //设置令牌生成策略(tokenStore在TokenConfig配置了),使用的是jwt
        services.setTokenStore(tokenStore);
        //设置令牌增强(固定用法-令牌Payload部分允许添加扩展数据,例如用户权限信息)
        TokenEnhancerChain chain=new TokenEnhancerChain();
        chain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter));
        //令牌增强对象设置到令牌生成
        services.setTokenEnhancer(chain);
        //设置令牌有效期
        services.setAccessTokenValiditySeconds(3600);//1小时
        //刷新令牌应用场景:一般在用户登录系统后,令牌快过期时,系统自动帮助用户刷新令牌,提高用户的体验感
        services.setRefreshTokenValiditySeconds(3600*72);//3天
        //配置客户端详情
        services.setClientDetailsService(clientDetailsService);
        return services;
    }

    // 设置客户端详情类似于用户详情
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //客户端id
                .withClient("gateway-client")
                //客户端秘钥
                .secret(passwordEncoder.encode("123456"))
                //设置权限
                .scopes("all")//all只是个名字而已和写abc效果相同
                //允许客户端进行的操作  里面的字符串千万不能写错
                .authorizedGrantTypes("password","refresh_token");
    }
    // 认证成功后的安全约束配置
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //认证通过后,允许客户端进行哪些操作
        security
                //公开oauth/token_key端点
                .tokenKeyAccess("permitAll()")
                //公开oauth/check_token端点
                .checkTokenAccess("permitAll()")
                //允许提交请求进行认证(申请令牌)
                .allowFormAuthenticationForClients();
    }
}

测试阶段

Postman测试jwt令牌获取

先成功启动好nacos和sentinel.再进行postman测试.

在postman里先输入地址?http://localhost:9000/auth/oauth/token?,然后点击下面的 Params 以进行参数的配置(也可以直接在地址栏输入参数,但为了直观与减小错误率,建议不这么做).具体参数内容如下图:

测试成功后上图结果区域的内容如下:

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzAwNjk3OTYsInVzZXJfbmFtZSI6ImphY2siLCJhdXRob3JpdGllcyI6WyJzeXM6cmVzOmNyZWF0ZSIsInN5czpyZXM6cmV0cml2ZSJdLCJqdGkiOiIwY2I5M2YyYS0xZjEzLTQxZDAtYmJmMi0xNGY4MDE2ZWNkYjUiLCJjbGllbnRfaWQiOiJnYXRld2F5LWNsaWVudCIsInNjb3BlIjpbImFsbCJdfQ.DUtdhGLSr-64JweJrOuxoPe-5mS62A1it--Z35_Rz0k",
    "token_type": "bearer",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJqYWNrIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6IjBjYjkzZjJhLTFmMTMtNDFkMC1iYmYyLTE0ZjgwMTZlY2RiNSIsImV4cCI6MTYzMDMyNTM5NiwiYXV0aG9yaXRpZXMiOlsic3lzOnJlczpjcmVhdGUiLCJzeXM6cmVzOnJldHJpdmUiXSwianRpIjoiYjRkMWVlZGItNDc1Mi00ODFmLTk2NTAtNjczOGI4YWYzZTVlIiwiY2xpZW50X2lkIjoiZ2F0ZXdheS1jbGllbnQifQ.fofDznmSjz2QuJmRZDBjc_NBdJPlFMD0a6rYaPUBLcY",
    "expires_in": 3600,
    "scope": "all",
    "jti": "0cb93f2a-1f13-41d0-bbf2-14f8016ecdb5"
}

浏览器测试登录流程

浏览器地址栏输入以下网址?http://localhost:8080/login-sso.html.

注意:这里也可以访问?http://localhost:8080/login.html,但是输入用户名和密码之后不能提交,因为需要的参数它还没有传递.

用户名和密码输入完成后,点击登录提交按钮,因为我们在login-sso.html里面设置了debugger,所以可以测试查看控制端输出的内容(必须先F12打开控制台才能进入到debugger阶段)

可以看到输出的结果和postman的结果是一样的.

登录成功后会切换到如下页面:

到这里,测试就已经成功了.

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-28 08:56:16  更:2021-08-28 08:57:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:09:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码