SpringCloud-OAuth2(四):改造篇

摘要:
本片主要关注Spring CloudOauth 2的实际转换,例如动态权限、集成JWT、更改默认url以及将客户端信息加载到数据库。继续上一篇文章:SpringCloud-OAuth2(I):基础章节SpringCloud-Outh2(II):实用章节SpringCloud-OAuth2(III):高级章节1:动态权限的通用权限验证机制如下:参考:https://zhuanlan.zhihu.com/p/144580287类型示例是硬编码的,例如在接口上添加注释:@PreAuthorizeHttpSecurity是在资源服务配置类中动态添加和配置的,例如。authorizeRequests()。anyRequest()。authenticated()。如果用户的权限发生更改,则无法满足以上两项要求。˃aClass){returntrue;}}1.2:FilterInvocationSecurityMetadataSource重写publicclassVipSecurityMetadataSourceimplements FilterInvocationSecurity MetadataSource{privatestaticfinalAntPathMatcherANT_PATH_MATCHER=newAntPathMatcher() ; privateSet<PermRoleEntity>permRoleEntitySet;privatefinalFilterInvocationSecurityMetadataSourcesuperMetadataSource;privatefinalVipSecurityOauthServicevipSecurityOauthService;PublicVipSecurityMetadataSource{this.superMetadataSource=superMetadataSource;this.vipSecurityOauthService=vipSecurityOauthService;}privatenoidloadPerms(){permRoleEntitySet=vipSecurityOauthService.loadPerms(;}/***返回可以访问请求的所有角色集合**/@OverridepublicCollection<ConfigAttribute>getAttributesthrowsIllegalArgumentException{loadPerms();FilterInvocationfi=object;Stringaccess_ uri=fi.getRequestUrl();用于{ifreturnpermRoleEntity.getConfigAttributeList();}returnsuperMetadataSource。getAttributes;}@OverridepublicCollectiongetAllConfigAttributes(){loadPerms();SetattributeSet=newHashSet();permRoleEntitySet.stream().map.forEach;returnattributeSet;}@Overridepublicbooleansupports(类˂?
本片主要讲SpringCloud Oauth2篇的实战改造,如动态权限、集成JWT、更改默认url、数据库加载client信息等改造。
同时,这应该也是我这系列博客的完结篇。

关于Oauth2,我也想说几句:
如果真的要应用到企业级项目当中去,必须要进行充足的准备,因为默认的配置、UI等很多都不是通用的(不适用于各个公司),
但是这套框架还好留了很多适配方法等,因此其需要修改的配置、处理器、方法重写等逻辑确实很多很多。

话不多说,正文开始。

承接前文:
SpringCloud-OAuth2(一):基础篇
SpringCloud-OAuth2(二):实战篇
SpringCloud-OAuth2(三):进阶篇

1:动态权限

常用的权限校验机制如以下几点:

参考:https://zhuanlan.zhihu.com/p/144580287

类型示例
硬编码如在接口上添加注解:@PreAuthorize("hasAnyRole('ROLE_ADMIN')")
HttpSecurity动态增加在资源服务配置类中配置,如:.authorizeRequests().anyRequest().authenticated()

如果出现用户所拥有的权限出现变化时,上述两种是无法满足的。


百度了几天后网上确实有同学给出了不错的示例,其工作机制如下:
drawingdrawing

1.1:AccessDecisionManager重写

@Component
public class VipAccessDecisionManager implements AccessDecisionManager {

    /**
     * 决定当前用户是否有权限访问该请求
     **/
    @Override
    public void decide(Authentication authentication, Object object,
                       Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        for (ConfigAttribute configAttribute : configAttributes) {
            //将访问所需资源或用户拥有资源进行比对
            String needAuthority = configAttribute.getAttribute();
            if (needAuthority == null) {
                continue;
            }
            for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
                if (needAuthority.trim().equals(grantedAuthority.getAuthority())) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("抱歉,您没有访问权限");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }

}

1.2:FilterInvocationSecurityMetadataSource重写

public class VipSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    private static final AntPathMatcher ANT_PATH_MATCHER = new AntPathMatcher();

    private Set<PermRoleEntity> permRoleEntitySet;

    private final FilterInvocationSecurityMetadataSource superMetadataSource;
    private final VipSecurityOauthService vipSecurityOauthService;

    public VipSecurityMetadataSource(FilterInvocationSecurityMetadataSource superMetadataSource, 
                                     VipSecurityOauthService vipSecurityOauthService) {
        this.superMetadataSource = superMetadataSource;
        this.vipSecurityOauthService = vipSecurityOauthService;
    }

    private void loadPerms() {
        permRoleEntitySet = vipSecurityOauthService.loadPerms();
    }

    /**
     * 返回能访问该请求的所有角色集合
     **/
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        loadPerms();

        FilterInvocation fi = (FilterInvocation) object;
        String access_uri = fi.getRequestUrl();

        for (PermRoleEntity permRoleEntity : permRoleEntitySet) {
            if (ANT_PATH_MATCHER.match(permRoleEntity.getAccessUri(), access_uri))
                return permRoleEntity.getConfigAttributeList();
        }

        return superMetadataSource.getAttributes(object);
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        loadPerms();

        Set<ConfigAttribute> attributeSet = new HashSet<>();
        permRoleEntitySet.stream().map(PermRoleEntity::getConfigAttributeList).forEach(attributeSet::addAll);
        return attributeSet;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

1.3:动态权限入口(自定义)

VipSecurityOauthService:权限的动态加载api
这里写成静态的,好参考。

@Component
public class VipSecurityOauthService {

    /**
     * 动态加载权限-角色信息
     **/
    public Set<PermRoleEntity> loadPerms() {
        Set<PermRoleEntity> permRoleEntitySet = new HashSet<>();
        permRoleEntitySet.add(new PermRoleEntity().setAccessUri("/demo/admin").setConfigAttributeList(SecurityConfig.createList("admin")));
        permRoleEntitySet.add(new PermRoleEntity().setAccessUri("/auth/**").setConfigAttributeList(SecurityConfig.createList("admin")));

        permRoleEntitySet.add(new PermRoleEntity().setAccessUri("/demo/sp-admin").setConfigAttributeList(SecurityConfig.createList("sp_admin")));

        return permRoleEntitySet;
    }

}

PermRoleEntity:url和角色对应关系

@Data
@Accessors(chain = true)
public class PermRoleEntity {

    /**
     * 访问的接口
     **/
    private String accessUri;

    /**
     * 可访问该接口的角色集合
     **/
    private List<ConfigAttribute> configAttributeList;
}

1.4:ResourceServer进行配置

drawing

1.5:测试

获取用户角色为admin的token,进行接口访问


①:用户admin角色的用户访问admin管理的接口
drawing


①:用户admin角色的用户访问sp-admin管理的接口
drawing

2:集成JWT

2.1:需要配置的bean

    @Bean
    public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
        return new JwtTokenStore(jwtAccessTokenConverter);
    }

    @Bean
    @Primary
    public AuthorizationServerTokenServices defaultTokenServices(TokenStore tokenStore,
                                                                 JwtAccessTokenConverter jwtAccessTokenConverter,
                                                                 ClientDetailsService clientDetailsService,
                                                                 AuthenticationManager authenticationManager) {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setSupportRefreshToken(true);
        defaultTokenServices.setTokenStore(tokenStore);
        defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter);
        defaultTokenServices.setAccessTokenValiditySeconds(3600);
        defaultTokenServices.setRefreshTokenValiditySeconds(7200);
        defaultTokenServices.setClientDetailsService(clientDetailsService);
        defaultTokenServices.setAuthenticationManager(authenticationManager);

        return defaultTokenServices;
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        // 签名密钥
        jwtAccessTokenConverter.setSigningKey("sign_key");
        // 验证密钥
        jwtAccessTokenConverter.setVerifier(new MacSigner("sign_key"));
        return jwtAccessTokenConverter;
    }

2.2:认证服务配置更改

drawing

tokenservice: 创建token、刷新token的地方。


获取token
drawing

2.3:OAuth2 Client

在前文中提到 OAuth2 Client Service 处理请求的时候是无法识别token的,需要远程提交给认证中心(OAuth2 Server)去识别token。
token换成JWT后客户端服务(OAuth2 Client Service)也可以识别token了,均需做如下配置:

    @Bean
    public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
        return new JwtTokenStore(jwtAccessTokenConverter);
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        // 签名密钥
        jwtAccessTokenConverter.setSigningKey("sign_key");
        // 验证密钥
        jwtAccessTokenConverter.setVerifier(new MacSigner("sign_key"));
        return jwtAccessTokenConverter;
    }

前文中提到的这项配置也可以丢弃了。
drawing


访问客户端服务的接口测试:
drawing

3:更改默认url

3.1:框架提供的URL路径:

默认url作用
/oauth/authorize授权端点
/oauth/token令牌端点
/oauth/confirm_access用户批准授权的端点
/oauth/error用于渲染授权服务器的错误
/oauth/check_token资源服务器解码access token
/oauth/check_token当使用JWT的时候,暴露公钥的端点

3.2:如何更改

可以按照下面这张方法进行重新转交接口地址:
drawing

自定义处理接口代码:

@RestController
public class AuthController extends WebApiController {
    private static final Logger logger = LoggerFactory.getLogger(AuthController.class);

    private final ConsumerTokenServices consumerTokenServices;
    private final TokenEndpoint tokenEndpoint;
    private final AuthorizationEndpoint authorizationRequest;
    private final WhitelabelApprovalEndpoint whitelabelApprovalEndpoint;
    private final WhitelabelErrorEndpoint whitelabelErrorEndpoint;

    public AuthController(ConsumerTokenServices consumerTokenServices, TokenEndpoint tokenEndpoint,
                          AuthorizationEndpoint authorizationRequest, WhitelabelApprovalEndpoint whitelabelApprovalEndpoint,
                          WhitelabelErrorEndpoint whitelabelErrorEndpoint) {
        this.consumerTokenServices = consumerTokenServices;
        this.tokenEndpoint = tokenEndpoint;
        this.authorizationRequest = authorizationRequest;
        this.whitelabelApprovalEndpoint = whitelabelApprovalEndpoint;
        this.whitelabelErrorEndpoint = whitelabelErrorEndpoint;
    }

    /**
     * 自定义登录接口
     */
    @PostMapping(value = "/login")
    public ResponseEntity<String> login(@RequestBody UserLoginDto userLoginDto, Principal principal) throws HttpRequestMethodNotSupportedException {
        Map<String, String> mapDto = ObjectMapperUtil.str2Obj(userLoginDto, new TypeReference<Map<String, String>>() {
        });
        mapDto.put(GrantTypeConstants.GRANT_TYPE, GrantTypeConstants.PASSWORD);

        OAuth2AccessToken token;
        try {
            token = tokenEndpoint.postAccessToken(principal, mapDto).getBody();
            if (token == null) {
                throw new ServiceException("登录异常");
            }
        } catch (Exception e) {
            if (e instanceof InvalidGrantException) {
                throw new ServiceException("用户名密码错误");
            } else {
                throw e;
            }
        }
        return response(WebApiResponse.ok(AuthToken.build(token)));
    }
}

3.3:测试


drawing4:数据库加载client信息

前文中以ProcessOn举例的QQ第三方登录就提到ProcessOn会向QQ申请一个client_id(客户端凭证),那么QQ第三方登录配置申请的入口必须将数据存放在数据库中,这样才能做到动态的新增、删除等,代码配置写死是不可能的。
注意:需要引入数据源、mysql驱动的依赖,并配置好数据源。

4.1:schema.sql

因为官方给的sql是hql的,我用的mysql8,因此做了一些类型上的修改。

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for clientdetails
-- ----------------------------
DROP TABLE IF EXISTS `clientdetails`;
CREATE TABLE `clientdetails`  (
  `appId` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `resourceIds` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `appSecret` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `scope` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `grantTypes` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `redirectUrl` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `authorities` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `access_token_validity` int(0) NULL DEFAULT NULL,
  `refresh_token_validity` int(0) NULL DEFAULT NULL,
  `additionalInformation` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `autoApproveScopes` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`appId`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of clientdetails
-- ----------------------------

-- ----------------------------
-- Table structure for oauth_access_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token`  (
  `token_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `token` binary(1) NULL DEFAULT NULL,
  `authentication_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `user_name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `client_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `authentication` binary(1) NULL DEFAULT NULL,
  `refresh_token` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`authentication_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;

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

-- ----------------------------
-- Table structure for oauth_approvals
-- ----------------------------
DROP TABLE IF EXISTS `oauth_approvals`;
CREATE TABLE `oauth_approvals`  (
  `userId` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `clientId` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `scope` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `status` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `expiresAt` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0),
  `lastModifiedAt` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0)
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of oauth_approvals
-- ----------------------------

-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details`  (
  `client_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `resource_ids` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `client_secret` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `scope` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `authorized_grant_types` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `web_server_redirect_uri` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `authorities` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `access_token_validity` int(0) NULL DEFAULT NULL,
  `refresh_token_validity` int(0) NULL DEFAULT NULL,
  `additional_information` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `autoapprove` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
INSERT INTO `oauth_client_details` VALUES ('admin', 'admin', 'tgMj02VG9dkpeKUN5lSWSsKotIt2yTIElkFcvsqLnwGOspNYhe+Teg==', 'test,all', 'authorization_code,client_credentials,password,implicit,refresh_token', 'http://www.baidu.com', 'admin', NULL, NULL, NULL, 'all');

-- ----------------------------
-- Table structure for oauth_client_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_token`;
CREATE TABLE `oauth_client_token`  (
  `token_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `token` binary(1) NULL DEFAULT NULL,
  `authentication_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `user_name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `client_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`authentication_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of oauth_client_token
-- ----------------------------

-- ----------------------------
-- Table structure for oauth_code
-- ----------------------------
DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code`  (
  `code` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `authentication` binary(1) NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of oauth_code
-- ----------------------------

-- ----------------------------
-- Table structure for oauth_refresh_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token`  (
  `token_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `token` binary(1) NULL DEFAULT NULL,
  `authentication` binary(1) NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;

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

SET FOREIGN_KEY_CHECKS = 1;

在这个表中配置数据即可,参考代码静态配置:
drawingdrawing

4.2:配置

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        clients.jdbc(dataSource);

    }

做好以上配置,认证服务就可以从数据库加载客户端凭证信息了。


本文参考博客:https://www.cnblogs.com/cjsblog/p/9184173.html

免责声明:文章转载自《SpringCloud-OAuth2(四):改造篇》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇jqgrid 事件说明Docker下安装 GRAYLOG 3.3下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

java微信二次第三方开发,token认证,消息事件接收,图文素材库,自定义菜单等功能

基于之前的文章SSM配置的项目:http://www.cnblogs.com/mangyang/p/5168291.html 来进行微信第三方开发, 微信二次开发,官方还是网上有很多介绍了,这里就不在进行讲述了 直接上干货。 首先 与微信对接,服务器配置,需要80端口和443端口开放的服务器,这里推荐 使用 python 的pagekite,一款反向代理...

FastReport.Net使用:[21]表格(Table)控件

对表格控件的一些常用操作  合并单元格:选择需要合并的单元格(按住Shitf多选),然后在右键菜单中选择【合并单元格】。         2.删除/插入行 鼠标移到在行头,当鼠标状态变为向右的箭头时点击鼠标选中改行,然后右键鼠标显示右键菜单,选择删除等操作。 另外,在报表树中也能进行删除等操作。 3.编辑数据: 双击需要编辑的单元格,弹出文本编辑器进...

kubeadmin搭建k8s集群

一、准备环境 准备环境: 角色 IP地址 k8s-master 192.168.56.21 k8s-node01 192.168.56.25 k8s-node02 192.168.56.26 所有节点环境初始化: 关闭防火墙: systemctl stop firewalld systemctl disable firewalld 关...

TVM在ARM GPU上优化移动深度学习

TVM在ARM GPU上优化移动深度学习 随着深度学习的巨大成功,将深度神经网络部署到移动设备的需求正在迅速增长。与在台式机平台上所做的类似,在移动设备中使用GPU可以提高推理速度和能源效率。但是,大多数现有的深度学习框架都不能很好地支持移动GPU。困难在于移动GPU架构和台式机GPU架构之间的差异。这意味着在移动GPU上进行优化需要付出特殊的努力。繁琐的...

04-表格 table(会使用)

第01阶段.前端基础.表格 HTML 第二天目标 能够利用表格、列表和表单完成注册页面的综合案例 能出说表格用来做什么的 能说出列表用来做什么的 能说出表单用来做什么的 为了让我们页面显示的更加整齐,我们需要学习三个表(表格、表单、列表) 表格 table(会使用) 目标: 理解: 能说出表格用来做什么的 表格的基本结构组成 应用: 能够熟练...

Jmeter请求头中添加了Authorization,但是在结果树里面的headers中显示没有获取到token

今天使用jmeter遇到了一个问题,看了大半天没找到原因。 前情回顾:新建了一个user_token文件,文件中存了1个参数token。用csv数据文件设置导入user_token文件,在请求头中Authorization中输入变量${token},但是在结果树中查看发现request headers中并没有获取到Authorization。    但是...