shiro 框架使用学习

简介

  • Shiro安全框架是Apache提供的一个强大灵活的安全框架
  • Shiro安全框架提供了认证、授权、企业会话管理、加密、缓存管理相关的功能,使用Shiro可以非常方便的完成项目的权限管理模块开发

Shiro的整体架构

在这里插入图片描述
1、Subject
​ Subject即主体(可以把当前用户理解为主体),外部应用与Subject进行交互,Subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。Subject在Shiro中是一个接口,接口中定义了很多认证授权相关的方法,外部程序通过Subject进行认证授,而Subject是通过SecurityManager安全管理器进行认证授权

2、Security Manager
​ SecurityManager即安全管理器,对全部的Subject进行安全管理,它是Shiro的核心,负责对所有的Subject进行安全管理。通过SecurityManager可以完成Subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。

3、Cryptography
​ Cryptography即密码管理,Shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。

4、Authenticator
​ Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,Shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。

5、Authorizer
​ Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。

6、realm
​ Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库,那么realm就需要从数据库获取用户身份信息。

7、sessionManager
​ sessionManager即会话管理,Shiro框架定义了一套会话管理,它不依赖web容器的session,所以Shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。

8、SessionDAO
​ SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。

9、CacheManager
​ CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。

shiro 使用

1.、shiro的配置类

/*** shiro权限管理的配置*/
@Configuration
public class ShiroConfig {/*** 安全管理器*/@Beanpublic DefaultWebSecurityManager securityManager(ShiroDatabaseRealm shiroDatabaseRealm,RememberMeManager rememberMeManager,CacheManager cacheManager,SessionManager sessionManager) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(shiroDatabaseRealm);securityManager.setCacheManager(cacheManager);securityManager.setRememberMeManager(rememberMeManager);securityManager.setSessionManager(sessionManager);return securityManager;}/*** 会话管理器*/@Beanpublic SessionManager sessionManager() {return new ServletContainerSessionManager();}/*** 缓存管理器*/@Beanpublic CacheManager getCacheShiroManager(EhCacheManagerFactoryBean ehcache) {EhCacheManager ehCacheManager = new EhCacheManager();ehCacheManager.setCacheManager(ehcache.getObject());return ehCacheManager;}/*** rememberMe管理器*/@Beanpublic CookieRememberMeManager rememberMeManager(SimpleCookie rememberMeCookie) {CookieRememberMeManager manager = new CookieRememberMeManager();manager.setCipherKey(Base64.decode("Z3VucwAAAAAAAAAAAAAAAA=="));manager.setCookie(rememberMeCookie);return manager;}/*** 记住密码Cookie*/@Beanpublic SimpleCookie rememberMeCookie() {SimpleCookie simpleCookie = new SimpleCookie("rememberMe");simpleCookie.setHttpOnly(true);simpleCookie.setMaxAge(7 * 24 * 60 * 60);//7天return simpleCookie;}/*** Shiro的过滤器链*/@Beanpublic ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();shiroFilter.setSecurityManager(securityManager);/*** 默认的登陆访问url*/shiroFilter.setLoginUrl("/login");/*** 登陆成功后跳转的url*/shiroFilter.setSuccessUrl("/");/*** 没有权限跳转的url*/shiroFilter.setUnauthorizedUrl("/global/error");/*** 自定义过滤器*/HashMap<String, Filter> myFilters = new HashMap<>();myFilters.put("user", new ShiroUserFilter());shiroFilter.setFilters(myFilters);/*** 配置shiro拦截器链** anon  不需要认证* authc 需要认证* user  验证通过或RememberMe登录的都可以** 当应用开启了rememberMe时,用户下次访问时可以是一个user,但不会是authc,因为authc是需要重新认证的** 顺序从上到下,优先级依次降低** api开头的接口,走rest api鉴权,不走shiro鉴权**/Map<String, String> hashMap = new LinkedHashMap<>();//NONE_PERMISSION_RES是一个集合里边存储了所有不需要过滤的路径,包括登录路径,错误路径等,将里边所有路径标志为anon,即访问不需要权限。for (String nonePermissionRe : NONE_PERMISSION_RES) {hashMap.put(nonePermissionRe, "anon");}//  剩下的路径  走自定义过滤器hashMap.put("/**", "user");shiroFilter.setFilterChainDefinitionMap(hashMap);return shiroFilter;}/*** Shiro生命周期处理器:* 用于在实现了Initializable接口的Shiro bean初始化时调用Initializable接口回调(例如:UserRealm)* 在实现了Destroyable接口的Shiro bean销毁时调用 Destroyable接口回调(例如:DefaultSecurityManager)*/@Beanpublic LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}/*** 启用shrio授权注解拦截方式,AOP式方法级权限检查*/@Bean@DependsOn("lifecycleBeanPostProcessor")public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}
}

2.在login controller中进行登录操作

在login controller中需要使用一个UsernamePasswordToken类型的token来存储用户名、密码、和remeberme ,token相当于一个令牌。
SecurityUtils.getSubject()得到subject, subject是一个非常重要的对象,包含了登录、注销、获取当前用户、检查角色、权限等操作的方法。
调用subject.login()方法将token作为参数会进入Shiro的安全管理器,并调用 Realm 中的 doGetAuthenticationInfo 方法。

 @RequestMapping(value = "/login", method = RequestMethod.POST)public String loginVali(String username, String password, String remember) {Subject subject = SecurityUtils.getSubject();// 1) TODO 准备 Token 数据UsernamePasswordToken token = new UsernamePasswordToken(username, password.toCharArray());// 2) TODO 开启“记住我”功能if(remember!=null&&remember.equals("on")){token.setRememberMe(true);}else {token.setRememberMe(false);}// 3) TODO 利用 subject 进行登录subject.login(token);// 4) TODO 记录登录日志 (暂时不做)return REDIRECT + "/";}

3.实现Realm类

需要实现Realmedia中的三个方法

  • doGetAuthenticationInfo()方法 ,在该方法中需要完成认证信息的准备。
  • setCredentialsMatcher()方法,需要在该方法中重新实现一个CredentialsMatcher()类中的接口用来提供验证密码的正确性,然后将新的CredentialsMatcher对象当作参数调用父类的setCredentialsMatcher()方法。
  • doGetAuthorizationInfo()方法,该方法中需要进行授权信息准备,准备好用户的权限信息,将来使用AOP进行权限过滤时会用到。

下边是自己实现的ShiroDatabaseRealm类

  • doGetAuthenticationInfo()方法中返回了一个SimpleAuthenticationInfo对象SimpleAuthenticationInfo 表示认证信息
    • principal 表示用户信息(一般就是一个用户对象,里面会包含角色信息)
    • credentail 表示密码信息(从数据库获取,一般是加密后的)
    • realmName表示当前的Realm的名字
  • SimpleAuthenticationInfo第一个参数传递了一个ShiorUser对象而不是直接的User对象是因为在ShiorUser对象中多了一些存储权限路径等信息的属性。
  • doGetAuthorizationInfo()方法返回一个SimpleAuthorizationInfo 对象,使用setRoles设置其角色集合,setStringPermissions设置权限集合(url访问列表)
@Component
@DependsOn({"userService", "deptService", "roleService"})
@Slf4j
public class ShiroDatabaseRealm extends AuthorizingRealm {@Autowiredprivate UserService userService;@Autowiredprivate DeptService deptService;@Autowiredprivate RoleService roleService;// TODO 1. 完成 认证信息 准备@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {// a. TODO 此 token 就是登录时封装的 token 对象UsernamePasswordToken token1=(UsernamePasswordToken)token;// b. TODO 可以利用 userService 中的方法获取 用户对象User user = userService.selectByAccount(token1.getUsername());// c. TODO 记得密码验证不需要在这里做,你要做的判断有两件事:// d. TODO 第一,判断 数据库中该用户是否存在,不存在,则抛出 shiro 的 UnknownAccountException 异常if(user==null) {throw new UnknownAccountException();}// e. TODO 第二,判断 该用户是否被冻结,如被冻结,则抛出 shiro 的 LockedAccountException 异常if(user.getStatus()=="FREEZE"){throw new LockedAccountException();}// f. TODO 将数据库用户信息 User 对象转换 为 ShiroUser 对象ShiroUser shiroUser=toShiroUser(user);// g. TODO 将认证信息:ShiroUser 对象、数据库密码、realm 名称(通过 getName() 得到) 封装至 SimpleAuthenticationInfo 并返回SimpleAuthenticationInfo root = new SimpleAuthenticationInfo(shiroUser, user.getPassword(), this.getName());/*注意* 验证密码的操作,是 shiro 框架完成的,不需要主动调用,只需要提供密码验证的 CredentialsMatcher 对象* 验证成功,进入 successUrl 验证失败进入 loginUrl* 成功后会将 SimpleAuthenticationInfo 中的 principal 信息存入 session, 以后可以通过 Subject 对象获得* 成功后还会做一些 RemeberMe cookie 的生成并返回操作,也无需我们干预*/return root;}/* ShiroUser 的作用1. 用来保存认证信息中的用户数据,即 principal,将来存入 session,以便在登录期间使用2. 其中除了用户数据,还包括了用户的角色信息,其属性都是根据需要自定义的3. 为什么不直接用 User ? 是因为认证过程中的很多属性(包括将来页面要显示的属性) User 对象中没有,因此用 ShiroUser 来保存更多的信息*/private ShiroUser toShiroUser(User user) {ShiroUser shiroUser = new ShiroUser();shiroUser.setId(user.getUserId());shiroUser.setAccount(user.getAccount());shiroUser.setDeptId(user.getDeptId());shiroUser.setDeptName(deptService.selectName(user.getDeptId()));shiroUser.setName(user.getName());shiroUser.setEmail(user.getEmail());shiroUser.setAvatar(user.getAvatar());String[] split = user.getRoleId().split(",");List<Long> roleIds = new ArrayList<>();List<String> roleNames = new ArrayList<>();for (String s : split) {Long roleId = Long.valueOf(s);roleIds.add(roleId);String roleName = roleService.selectName(roleId);roleNames.add(roleName);}shiroUser.setRoleList(roleIds);shiroUser.setRoleNames(roleNames);log.debug("==============> user {} roles: {}", user.getName(), shiroUser.getRoleNames());return shiroUser;}// TODO 2. 提供密码验证 CredentialsMatcher@Overridepublic void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {CredentialsMatcher matcher = new CredentialsMatcher() {@Overridepublic boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {// TODO token 是表单提交过来的数据, info 是在 doGetAuthenticationInfo 步骤返回的认证信息UsernamePasswordToken token1=(UsernamePasswordToken)token;char[] password = token1.getPassword();String pass_user = new String(password);String pass_databases = info.getCredentials().toString();// TODO 使用 BCrypt 算法验证密码的正确性,返回值即表示验证是否通过return  BCrypt.checkpw(pass_user, pass_databases);// 验证不通过会由框架抛出 IncorrectCredentialsException 异常}};super.setCredentialsMatcher(matcher);}// TODO 3. 完成 授权信息 准备// 当需要进行权限验证时,会调用 doGetAuthorizationInfo 获得当前用户(已认证)的权限信息,只会执行一次,放入缓存当中@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();// a. TODO 获取用户的所有 url 访问权限,可以通过 roleService.selectPermissionURL 获取一个角色的 url 访问列表// 注意,一个用户可能会有多个角色LinkedHashSet<String> set = new LinkedHashSet<>();for (Long roleId : shiroUser.getRoleList()) {List<String> strings = roleService.selectPermissionURL(roleId);set.addAll(strings);}// b. TODO 准备一个 SimpleAuthorizationInfo 对象,应设置其角色集合,权限集合(url访问列表),并返回/* 注意,这些集合中都是字符串表示的角色和权限后续可以通过 Subject 对象中的相关方法来使用这里准备的数据,如:* subject.hasRole(角色名) 判断用户是否有某个角色* subject.isPermitted(权限名) 判断用户是否有某个权限* 更多方法,参考 shiro 的 Subject 接口说明*/LinkedHashSet<String> roleNames = new LinkedHashSet<>();roleNames.addAll(shiroUser.getRoleNames());SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.setRoles(roleNames);info.setStringPermissions(set);return info;}
}

4. 在AOP中进行权限过滤

在需要进行权限过滤的controller方法上加@Permission注解。
若该方法必须某个角色才能访问则@Permission(“角色名”);

  • 得到subject
    Subject subject = SecurityUtils.getSubject();
  • 判断是否含有所有角色
    subject.hasAllRoles()
  • 判断是否含有URL
    subject.isPermitted()

/*** 权限检查的aop*/
// 0. TODO 打开 @Aspect 注解
@Aspect
@Component
@Order(200)
@Slf4j
public class ShiroPermissionAop {// 1. TODO 控制器内需要权限控制的方法上都加了 @Permission 自定义注解,添加合适的切点@Around("@annotation(cn.stylefeng.guns.core.common.annotion.Permission)")public Object doPermission(ProceedingJoinPoint pjp) throws Throwable {// a. 获取当前的请求路径(已实现)String requestURI = getRequestURI();// b. 获取当前的控制器方法对象(已实现)Method method = getMethod(pjp);// c. TODO 拿到方法的 Permission 注解,做进一步判断Permission annotation= method.getAnnotation(Permission.class);String[] roleNames = annotation.value();// d. TODO 分支1,如果 Permission 上有角色,调用 checkRoles 进一步判断if(annotation.value().length>0){boolean pass= checkRoles(roleNames);;if(!pass){throw new NoPermissionException("没有权限");}}// e. TODO 分支2,如果没有角色,那么进行所有路径匹配检查,调用 checkPermission 进一步判断else{boolean pass= checkPermission(requestURI);if(!pass){throw new NoPermissionException("没有权限");}}// f. TODO 以上分支,检查通过,使用 pjp 放行, 不通过抛出 NoPermissionException// 注意放行的话应该 将 pjp 执行目标方法的结果返回return pjp.proceed();}private Method getMethod(ProceedingJoinPoint point) {MethodSignature ms = (MethodSignature) point.getSignature();return ms.getMethod();}private String getRequestURI() {HttpServletRequest request = HttpContext.getRequest();String requestURI = request.getRequestURI().replaceFirst(ConfigListener.getConf().get("contextPath"), "");log.debug("===============> current uri: {}", requestURI);return requestURI;}public boolean checkRoles(Object[] permissions) {String[] roleNames = (String[]) permissions;Subject subject = SecurityUtils.getSubject();return subject.hasAllRoles(Arrays.asList(roleNames));}private boolean checkPermission(String requestURI) {Subject subject = SecurityUtils.getSubject();return  subject.isPermitted(requestURI);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/132347.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mysql Cluster (NDB - Network Database) - 分布式

Mysql高可用架构 复制&#xff08;Replication&#xff09; 是本文中所有 MySQL 技术的基础。包括&#xff1a;异步复制、半同步复制&#xff0c;增强半同步复制。InnoDB 副本集&#xff08;MySQL InnoDB ReplicaSet&#xff09; 无缝衔接其他 MySQL 官方提供的应用程序&#…

没有MES管理系统,先用数据采集设备能有用吗

在当前的数字化时代&#xff0c;企业纷纷意识到了数字化转型的重要性。数据被誉为新型生产要素&#xff0c;对于企业的运营和决策具有至关重要的作用。在数字化转型的过程中&#xff0c;许多企业面临着一个共同的问题&#xff1a;如何获取所需的数据&#xff1f; 有两家企业在…

偶数矩阵判断【C语言作业】

题目 若一个布尔矩阵所有行和所有列的和都是偶数&#xff0c;则称为偶数矩阵。请编写一个程序&#xff0c;判断一个布尔矩阵是否是偶数矩阵。 要求&#xff1a; &#xff08;1&#xff09;输入:首先输入一个正整数n(n<100),代表该矩阵的大小&#xff0c;接下来是n行n列的矩…

TCP/IP协议群

TCP/IP协议群 什么是TCP/IP协议群 从字面意义上讲&#xff0c;有人可能会认为 TCP/IP 是指 TCP 和 IP 两种协议。实际生活当中有时也确实就是指这两种协议。然而在很多情况下&#xff0c;它只是利用 IP 进行通信时所必须用到的协议群的统称。具体来说&#xff0c;IP 或 ICMP、…

微信小程序 uCharts的使用方法

一、背景 微信小程序项目需要渲染一个柱状图&#xff0c;使用uCharts组件完成 uCharts官网指引&#x1f449;&#xff1a;uCharts官网 - 秋云uCharts跨平台图表库 二、实现效果 三、具体使用 进入官网查看指南&#xff0c;有两种方式进行使用&#xff1a;分别是原生方式与组…

pytorch实现 --- 手写数字识别

本篇文章是博主在人工智能等领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对人工智能等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅解。文章分类在Pytorch&#xff…

Prometheus接入AlterManager配置企业微信告警(基于K8S环境部署)

文章目录 一、创建企业微信机器人二、配置AlterManager告警发送至企业微信三、Prometheus接入AlterManager配置四、部署PrometheusAlterManager(放到一个Pod中)五、测试告警 注意&#xff1a;请基于 PrometheusGrafana监控K8S集群(基于K8S环境部署)文章之上做本次实验。 一、创…

Sui浏览器现支持查看已验证的Move源代码

Sui浏览器现在包含了Sui框架包的源代码视图&#xff0c;这是一个备受欢迎的功能。这个新的“已验证源代码&#xff08;Source Verified&#xff09;”选项卡位于现有的“字节码&#xff08;Bytecode&#xff09;”选项卡旁边&#xff0c;使开发人员可以点击查看以下包的源代码&…

RFID技术在固定资产的应用

作为RFID系统中不可或缺的关键组件&#xff0c;读写器在固定资产管理中扮演着重要的角色。它利用RFID技术&#xff0c;能够迅速而有效地捕获、记录和跟踪资产信息&#xff0c;以实现更为高效和准确的资产管理。在本文中&#xff0c;我们将深入探讨RFID技术在固定资产管理领域的…

【广州华锐互动】VR历史古城复原:沉浸式体验古代建筑,感受千年风华!

在科技日新月异的今天&#xff0c;虚拟现实&#xff08;VR&#xff09;技术已经成为了我们生活中不可或缺的一部分。从娱乐游戏到医疗健康&#xff0c;从教育培训到房地产销售&#xff0c;VR技术的应用领域日益广泛。而近年来&#xff0c;VR技术在文化遗产保护和古迹复原方面的…

哈夫曼编码与解码,基于Python实现

from itertools import count from collections import Counter from heapq import heapify, heappush, heappopdef huffman_tree(s):# 统计每个字符出现的次数s Counter(s) # 计算可迭代序列中元素的数量&#xff0c;返回字典类型数据c…

Web自动化测试框架Selenium

什么是自动化测试 自动化测试就是&#xff0c;假定一些预设值&#xff0c;由机器自动化完成的测试。 web自动化测试&#xff0c;将自动化测试技术艺能用到web测试中&#xff0c;是假定一些预设值&#xff0c;由程序驱动浏览器来完成web程序的测试&#xff0c;如下图&#xff1…

chrome 防止http自动转https的方法

1. 左上角&#xff0c;单击地址栏左边 2. 然后点击网站设置 3. 不安全内容改为【允许】 4. 然后以后访问此网站时&#xff0c;就不会再自动跳转为https了

oracle查询数据库内全部的表名、列明、注释、数据类型、长度、精度等

Oracle查询数据库内全部的表名、列明、注释、数据类型、长度、精度 SELECT a.TABLE_NAME 表名, row_number() over(partition by a.TABLE_NAME order by a.COLUMN_NAME desc) 字段顺序,a.COLUMN_NAME 列名, b.COMMENTS 注释,a.DATA_TYPE 数据类型, a.DATA_LENGTH 长度,DATA_SC…

C语言编写学生成绩管理系统-要求有增删改查(C语言基础题-8道)

文章目录 1-5题题目例题8&#xff08;难度★ ★ ★ ★ ☆&#xff09; 答案例题8答案1解析 答案2解析 1-5题 C语言基础例题1-3题-指针篇 C语言基础例题4-5题-二维数组篇 C语言基础例题6-7题-结构体篇 题目 例题8&#xff08;难度★ ★ ★ ★ ☆&#xff09; 编写一个学生成…

二、Hadoop分布式系统基础架构

1、分布式 分布式体系中&#xff0c;会存在众多服务器&#xff0c;会造成混乱等情况。那如何让众多服务器一起工作&#xff0c;高效且不出现问题呢&#xff1f; 2、调度 &#xff08;1&#xff09;架构 在大数据体系中&#xff0c;分布式的调度主要有2类架构模式&#xff1a…

使用Selenium Grid远程执行测试

我们将在同一台工作电脑上&#xff0c;分别启动主控&#xff08;Hub&#xff09;和节点&#xff08;Node&#xff09;2个Selenium Grid服务&#xff0c;IP地址均使用环回地址127.0.0.1&#xff0c;端口分别为4444和5555。开始以下操作前&#xff0c;请确认你的机器上已经安装、…

【MySQL基本功系列】第一篇 先熟悉MySQL的运行逻辑

​ 我将推出一系列关于MySQL的博客文章&#xff0c;涵盖了从入门到深入底层的原理。这些文章将包括MySQL的运行逻辑、InnoDB存储引擎、SQL优化、undo log、bin log等多个方面的知识。希望这些文章能为你提供宝贵的信息和洞见&#xff0c;并帮助你更好地理解和应用MySQL。同时&a…

打造高效运营底座,极智嘉一体化软件系统彰显科技威能

在仓储成本和物流需求日益增加的今天&#xff0c;创新且高效的物流机器人解决方案能够显著提升物流运营效率&#xff0c;降低物流成本&#xff0c;实现智能化、精益化、一体化的物流管理。全球仓储机器人引领者极智嘉(Geek)以「一套系统&#xff0c;天生全能」为准则&#xff0…

阿里巴巴1688商品详情 API 接口示例

1688.item_get 公共参数 请求地址: https://o0b.cn/anzexi 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,item_get,item_…