基于shiro的改造集成真正支持restful请求

这个模块分离至上上上一篇api权限管理系统与前后端分离实践,感觉那样太长了找不到重点,分离出来要好点。


首先说明设计的这个安全体系是是RBAC(基于角色的权限访问控制)授权模型,即用户--角色--资源,用户不直接和权限打交道,角色拥有资源,用户拥有这个角色就有权使用角色所用户的资源。所有这里没有权限一说,签发jwt里面也就只有用户所拥有的角色而没有权限。

为啥说是真正的restful风格集成,虽说shiro对rest不友好但他本身是有支持rest集成的filter–HttpMethodPermissionFilter,这个shiro rest的 风格拦截器,会自动根据请求方法构建权限字符串( GET=read,POST=create,PUT=update,DELETE=delete)构建权限字符串;eg: /users=rest[user] , 会 自动拼接出user:read,user:create,user:update,user:delete”权限字符串进行权限匹配(所有都得匹配,isPermittedAll)。

但是这样感觉不利于基于jwt的角色的权限控制,在细粒度上验权url(即支持get,post,delete鉴别)就更没法了(个人见解)。打个比方:我们对一个用户签发的jwt写入角色列(role_admin,role_customer)。对不同request请求:url="api/resource/",httpMethod="GET"url="api/resource",httpMethod="POST",在基于角色-资源的授权模型中,这两个url相同的请求对HttpMethodPermissionFilter是一种请求,用户对应的角色拥有的资源url=”api/resource”,只要请求的url是”api/resource”,不论它的请求方式是什么,都会判定通过这个请求,这在restful风格的api中肯定是不可取的,对同一资源有些角色可能只要查询的权限而没有修改增加的权限。

可能会说在jwt中再增加权限列就好了嘛,但是在基于用户-资源的授权模型中,虽然能判别是不同的请求,但是太麻烦了,对每个资源我们都要设计对应的权限列然后再塞入到jwt中,对每个用户都要单独授权资源这也是不可取的。

对shiro的改造这里自定义了一些规则:
shiro过滤器链的url=url+"=="+httpMethod
eg:对于url="api/resource/",httpMethod="GET"的资源,其拼接出来的过滤器链匹配url=api/resource==GET
这样对相同的url而不同的访问方式,会判定为不同的资源,即资源不再简单是url,而是url和httpMethod的组合。基于角色的授权模型中,角色所拥有的资源形式为url+"=="+httpMethod
这里改变了过滤器的过滤匹配url规则,重写PathMatchingFilterChainResolver的getChain方法,增加对上述规则的url的支持。

/* ** @Author tomsun28* @Description * @Date 21:12 2018/4/20*/
public class RestPathMatchingFilterChainResolver extends PathMatchingFilterChainResolver {private static final Logger LOGGER = LoggerFactory.getLogger(RestPathMatchingFilterChainResolver.class);public RestPathMatchingFilterChainResolver() {super();}public RestPathMatchingFilterChainResolver(FilterConfig filterConfig) {super(filterConfig);}/* ** @Description 重写filterChain匹配* @Param [request, response, originalChain]* @Return javax.servlet.FilterChain*/@Overridepublic FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {FilterChainManager filterChainManager = this.getFilterChainManager();if (!filterChainManager.hasChains()) {return null;} else {String requestURI = this.getPathWithinApplication(request);Iterator var6 = filterChainManager.getChainNames().iterator();String pathPattern;boolean flag = true;String[] strings = null;do {if (!var6.hasNext()) {return null;}pathPattern = (String)var6.next();strings = pathPattern.split("==");if (strings.length == 2) {// 分割出url+httpMethod,判断httpMethod和request请求的method是否一致,不一致直接falseif (WebUtils.toHttp(request).getMethod().toUpperCase().equals(strings[1].toUpperCase())) {flag = false;} else {flag = true;}} else {flag = false;}pathPattern = strings[0];} while(!this.pathMatches(pathPattern, requestURI) || flag);if (LOGGER.isTraceEnabled()) {LOGGER.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "].  Utilizing corresponding filter chain...");}if (strings.length == 2) {pathPattern = pathPattern.concat("==").concat(WebUtils.toHttp(request).getMethod().toUpperCase());}return filterChainManager.proxy(originalChain, pathPattern);}}}

重写PathMatchingFilter的路径匹配方法pathsMatch(),加入httpMethod支持。

/* ** @Author tomsun28* @Description 重写过滤链路径匹配规则,增加REST风格post,get.delete,put..支持* @Date 23:37 2018/4/19*/
public abstract class BPathMatchingFilter extends PathMatchingFilter {public BPathMatchingFilter() {}/* ** @Description 重写URL匹配  加入httpMethod支持* @Param [path, request]* @Return boolean*/@Overrideprotected boolean pathsMatch(String path, ServletRequest request) {String requestURI = this.getPathWithinApplication(request);// path: url==method eg: http://api/menu==GET   需要解析出path中的url和httpMethodString[] strings = path.split("==");if (strings.length <= 1) {// 分割出来只有URLreturn this.pathsMatch(strings[0], requestURI);} else {// 分割出url+httpMethod,判断httpMethod和request请求的method是否一致,不一致直接falseString httpMethod = WebUtils.toHttp(request).getMethod().toUpperCase();return httpMethod.equals(strings[1].toUpperCase()) && this.pathsMatch(strings[0], requestURI);}}
}

这样增加httpMethod的改造就完成了,重写ShiroFilterFactoryBean使其使用改造后的chainResolver:RestPathMatchingFilterChainResolver

/* ** @Author tomsun28* @Description rest支持的shiroFilterFactoryBean* @Date 21:35 2018/4/20*/
public class RestShiroFilterFactoryBean extends ShiroFilterFactoryBean {private static final Logger LOGGER = LoggerFactory.getLogger(RestShiroFilterFactoryBean.class);public RestShiroFilterFactoryBean() {super();}@Overrideprotected AbstractShiroFilter createInstance() throws Exception {LOGGER.debug("Creating Shiro Filter instance.");SecurityManager securityManager = this.getSecurityManager();String msg;if (securityManager == null) {msg = "SecurityManager property must be set.";throw new BeanInitializationException(msg);} else if (!(securityManager instanceof WebSecurityManager)) {msg = "The security manager does not implement the WebSecurityManager interface.";throw new BeanInitializationException(msg);} else {FilterChainManager manager = this.createFilterChainManager();RestPathMatchingFilterChainResolver chainResolver = new RestPathMatchingFilterChainResolver();chainResolver.setFilterChainManager(manager);return new RestShiroFilterFactoryBean.SpringShiroFilter((WebSecurityManager)securityManager, chainResolver);}}private static final class SpringShiroFilter extends AbstractShiroFilter {protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {if (webSecurityManager == null) {throw new IllegalArgumentException("WebSecurityManager property cannot be null.");} else {this.setSecurityManager(webSecurityManager);if (resolver != null) {this.setFilterChainResolver(resolver);}}}}
}

上面是一些核心的代码片段,更多请看项目代码。

对用户账户登录注册的过滤filter:PasswordFilter

/* ** @Author tomsun28* @Description 基于 用户名密码 的认证过滤器* @Date 20:18 2018/2/10*/
public class PasswordFilter extends AccessControlFilter {private static final Logger LOGGER = LoggerFactory.getLogger(PasswordFilter.class);private StringRedisTemplate redisTemplate;@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {Subject subject = getSubject(request,response);// 如果其已经登录,再此发送登录请求if(null != subject && subject.isAuthenticated()){return true;}//  拒绝,统一交给 onAccessDenied 处理return false;}@Overrideprotected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {// 判断若为获取登录注册加密动态秘钥请求if (isPasswordTokenGet(request)) {//动态生成秘钥,redis存储秘钥供之后秘钥验证使用,设置有效期5秒用完即丢弃String tokenKey = CommonUtil.getRandomString(16);try {redisTemplate.opsForValue().set("PASSWORD_TOKEN_KEY_"+request.getRemoteAddr().toUpperCase(),tokenKey,5, TimeUnit.SECONDS);// 动态秘钥response返回给前端Message message = new Message();message.ok(1000,"issued tokenKey success").addData("tokenKey",tokenKey);RequestResponseUtil.responseWrite(JSON.toJSONString(message),response);}catch (Exception e) {LOGGER.warn(e.getMessage(),e);// 动态秘钥response返回给前端Message message = new Message();message.ok(1000,"issued tokenKey fail");RequestResponseUtil.responseWrite(JSON.toJSONString(message),response);}return false;}// 判断是否是登录请求if(isPasswordLoginPost(request)){AuthenticationToken authenticationToken = createPasswordToken(request);Subject subject = getSubject(request,response);try {subject.login(authenticationToken);//登录认证成功,进入请求派发json web token url资源内return true;}catch (AuthenticationException e) {LOGGER.warn(authenticationToken.getPrincipal()+"::"+e.getMessage(),e);// 返回response告诉客户端认证失败Message message = new Message().error(1002,"login fail");RequestResponseUtil.responseWrite(JSON.toJSONString(message),response);return false;}catch (Exception e) {LOGGER.error(e.getMessage(),e);// 返回response告诉客户端认证失败Message message = new Message().error(1002,"login fail");RequestResponseUtil.responseWrite(JSON.toJSONString(message),response);return false;}}// 判断是否为注册请求,若是通过过滤链进入controller注册if (isAccountRegisterPost(request)) {return true;}// 之后添加对账户的找回等// response 告知无效请求Message message = new Message().error(1111,"error request");RequestResponseUtil.responseWrite(JSON.toJSONString(message),response);return false;}private boolean isPasswordTokenGet(ServletRequest request) {
//        String tokenKey = request.getParameter("tokenKey");String tokenKey = RequestResponseUtil.getParameter(request,"tokenKey");return (request instanceof HttpServletRequest)&& ((HttpServletRequest) request).getMethod().toUpperCase().equals("GET")&& null != tokenKey && "get".equals(tokenKey);}private boolean isPasswordLoginPost(ServletRequest request) {
//        String password = request.getParameter("password");
//        String timestamp = request.getParameter("timestamp");
//        String methodName = request.getParameter("methodName");
//        String appId = request.getParameter("appId");Map<String ,String> map = RequestResponseUtil.getRequestParameters(request);String password = map.get("password");String timestamp = map.get("timestamp");String methodName = map.get("methodName");String appId = map.get("appId");return (request instanceof HttpServletRequest)&& ((HttpServletRequest) request).getMethod().toUpperCase().equals("POST")&& null != password&& null != timestamp&& null != methodName&& null != appId&& methodName.equals("login");}private boolean isAccountRegisterPost(ServletRequest request) {
//        String uid = request.getParameter("uid");
//        String methodName = request.getParameter("methodName");
//        String username = request.getParameter("username");
//        String password = request.getParameter("password");Map<String ,String> map = RequestResponseUtil.getRequestParameters(request);String uid = map.get("uid");String username = map.get("username");String methodName = map.get("methodName");String password = map.get("password");return (request instanceof HttpServletRequest)&& ((HttpServletRequest) request).getMethod().toUpperCase().equals("POST")&& null != username&& null != password&& null != methodName&& null != uid&& methodName.equals("register");}private AuthenticationToken createPasswordToken(ServletRequest request) {//        String appId = request.getParameter("appId");
//        String password = request.getParameter("password");
//        String timestamp = request.getParameter("timestamp");Map<String ,String> map = RequestResponseUtil.getRequestParameters(request);String appId = map.get("appId");String timestamp = map.get("timestamp");String password = map.get("password");String host = request.getRemoteAddr();String tokenKey = redisTemplate.opsForValue().get("PASSWORD_TOKEN_KEY_"+host.toUpperCase());return new PasswordToken(appId,password,timestamp,host,tokenKey);}public void setRedisTemplate(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}}

支持restful风格的jwt鉴权filter:BJwtFilter

/* ** @Author tomsun28* @Description 支持restful url 的过滤链  JWT json web token 过滤器,无状态验证* @Date 0:04 2018/4/20*/
public class BJwtFilter extends BPathMatchingFilter {private static final Logger LOGGER = LoggerFactory.getLogger(BJwtFilter.class);private StringRedisTemplate redisTemplate;private AccountService accountService;protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object mappedValue) throws Exception {Subject subject = getSubject(servletRequest,servletResponse);// 判断是否为JWT认证请求if ((null == subject || !subject.isAuthenticated()) && isJwtSubmission(servletRequest)) {AuthenticationToken token = createJwtToken(servletRequest);try {subject.login(token);
//                return this.checkRoles(subject,mappedValue) && this.checkPerms(subject,mappedValue);return this.checkRoles(subject,mappedValue);}catch (AuthenticationException e) {LOGGER.info(e.getMessage(),e);// 如果是JWT过期if (e.getMessage().equals("expiredJwt")) {// 这里初始方案先抛出令牌过期,之后设计为在Redis中查询当前appId对应令牌,其设置的过期时间是JWT的两倍,此作为JWT的refresh时间// 当JWT的有效时间过期后,查询其refresh时间,refresh时间有效即重新派发新的JWT给客户端,// refresh也过期则告知客户端JWT时间过期重新认证// 当存储在redis的JWT没有过期,即refresh time 没有过期String appId = WebUtils.toHttp(servletRequest).getHeader("appId");String jwt = WebUtils.toHttp(servletRequest).getHeader("authorization");String refreshJwt = redisTemplate.opsForValue().get("JWT-SESSION-"+appId);if (null != refreshJwt && refreshJwt.equals(jwt)) {// 重新申请新的JWT// 根据appId获取其对应所拥有的角色(这里设计为角色对应资源,没有权限对应资源)String roles = accountService.loadAccountRole(appId);long refreshPeriodTime = 36000L;  //seconds为单位,10 hoursString newJwt = JsonWebTokenUtil.issueJWT(UUID.randomUUID().toString(),appId,"token-server",refreshPeriodTime >> 2,roles,null, SignatureAlgorithm.HS512);// 将签发的JWT存储到Redis: {JWT-SESSION-{appID} , jwt}redisTemplate.opsForValue().set("JWT-SESSION-"+appId,newJwt,refreshPeriodTime, TimeUnit.SECONDS);Message message = new Message().ok(1005,"new jwt").addData("jwt",newJwt);RequestResponseUtil.responseWrite(JSON.toJSONString(message),servletResponse);return false;}else {// jwt时间失效过期,jwt refresh time失效 返回jwt过期客户端重新登录Message message = new Message().error(1006,"expired jwt");RequestResponseUtil.responseWrite(JSON.toJSONString(message),servletResponse);return false;}}// 其他的判断为JWT错误无效Message message = new Message().error(1007,"error Jwt");RequestResponseUtil.responseWrite(JSON.toJSONString(message),servletResponse);return false;}catch (Exception e) {// 其他错误LOGGER.warn(servletRequest.getRemoteAddr()+"JWT认证"+e.getMessage(),e);// 告知客户端JWT错误1005,需重新登录申请jwtMessage message = new Message().error(1007,"error jwt");RequestResponseUtil.responseWrite(JSON.toJSONString(message),servletResponse);return false;}}else {// 请求未携带jwt 判断为无效请求Message message = new Message().error(1111,"error request");RequestResponseUtil.responseWrite(JSON.toJSONString(message),servletResponse);return false;}}protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {Subject subject = getSubject(servletRequest,servletResponse);// 未认证的情况if (null == subject || !subject.isAuthenticated()) {// 告知客户端JWT认证失败需跳转到登录页面Message message = new Message().error(1006,"error jwt");RequestResponseUtil.responseWrite(JSON.toJSONString(message),servletResponse);}else {//  已经认证但未授权的情况// 告知客户端JWT没有权限访问此资源Message message = new Message().error(1008,"no permission");RequestResponseUtil.responseWrite(JSON.toJSONString(message),servletResponse);}// 过滤链终止return false;}private boolean isJwtSubmission(ServletRequest request) {String jwt = RequestResponseUtil.getHeader(request,"authorization");String appId = RequestResponseUtil.getHeader(request,"appId");return (request instanceof HttpServletRequest)&& !StringUtils.isEmpty(jwt)&& !StringUtils.isEmpty(appId);}private AuthenticationToken createJwtToken(ServletRequest request) {Map<String,String> maps = RequestResponseUtil.getRequestHeaders(request);String appId = maps.get("appId");String ipHost = request.getRemoteAddr();String jwt = maps.get("authorization");String deviceInfo = maps.get("deviceInfo");return new JwtToken(ipHost,deviceInfo,jwt,appId);}// 验证当前用户是否属于mappedValue任意一个角色private boolean checkRoles(Subject subject, Object mappedValue){String[] rolesArray = (String[]) mappedValue;return rolesArray == null || rolesArray.length == 0 || Stream.of(rolesArray).anyMatch(role -> subject.hasRole(role.trim()));}// 验证当前用户是否拥有mappedValue任意一个权限private boolean checkPerms(Subject subject, Object mappedValue){String[] perms = (String[]) mappedValue;boolean isPermitted = true;if (perms != null && perms.length > 0) {if (perms.length == 1) {if (!subject.isPermitted(perms[0])) {isPermitted = false;}} else {if (!subject.isPermittedAll(perms)) {isPermitted = false;}}}return isPermitted;}public void setRedisTemplate(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}public void setAccountService(AccountService accountService) {this.accountService = accountService;}
}

realm数据源,数据提供service,匹配matchs,自定义token,spring集成shiro配置等其他详见项目代码。
最后项目实现了基于jwt的动态restful api权限认证。


效果展示

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

github:
bootshiro
usthe

码云:
bootshiro
usthe


持续更新。。。。。。

分享一波阿里云代金券快速上云

转载请注明 from tomsun28

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

相关文章

  1. 【unity】代码+资源反编译教程

    来自:http://bbs.9ria.com/thread-401140-1-1.html 首先感谢 雨松MOMO 的一篇帖子 教我们怎么提取 .ipa 中的游戏资源。教我们初步的破解unity3d资源的基本方法 附上原帖的链接:http://www.xuanyusong.com/archives/2584下面我会从头介绍一下提取的全过程:步骤一:首先从 ht…...

    2024/5/2 4:33:28
  2. 【Java】利用poi插件,把Excel内容读入Java,把Java中的内容输出到Excel

    上次在《【Java】无须额外的包,把Java中的内容输出到Excel中,无乱码,绝对兼容Excel2003与2007》(点击打开链接)给出了一种无须额外的Excel包就能够输出Java的内容的到Excel的方案,但是整个方案都是在操作能被Excel读取的.xml文件。构造.xml。这种方法编程起来可能有点麻烦…...

    2024/4/27 10:48:45
  3. Java英语单词大全

    Java基础常见英语词汇(70个)OO: object-oriented ,面向对象 OOP: object-oriented programming,面向对象编程JDK:Java development kit, java开发工具包 JVM:java virtual machine ,java虚拟机Compile:编绎 …...

    2024/5/1 4:01:18
  4. FWT专练(模板 + Card Game + [hdu 5909]tree cutting)

    bhys,蒟蒻太菜,FWT学习博客是真的证明不来 只能保存下来模板。。。文章目录模板总集or(或)and(与)xor(异或)T1:Card GametitlesolutioncodeT2:Tree Cuttingtitlesolutioncode 模板总集 or(或) ① void FWT_or( int l, int r ) {if( l == r ) return;int mid = ( l …...

    2024/4/20 7:07:32
  5. C++函数模板与类模板的区别

    类模板:C++ 除了支持函数模板,还支持类模板(Class Template)。函数模板中定义的类型参数可以用在函数声明和函数定义中,类模板中定义的类型参数可以用在类声明和类实现中。类模板的目的同样是将数据的类型参数化。 声明类模板的语法为:template<typename 类型参数1 , …...

    2024/4/30 17:51:00
  6. 小白必知的数据库知识点一二三

    对数据库的操作可以概括为就是向数据库中添加、删除、修改和查询数据,其中查询功能最为复杂。 SQLite 一款轻型的嵌入式数据库,占用资源及其低,这是它受人青睐的原因之一,在嵌入式设备(如手机)中只需要几百 K 的内存即可。它不仅支持数据库通用的增删改查,还支持事务功能…...

    2024/4/29 12:31:24
  7. 安卓Android反编译教程

    1、自行下载反编译并解压得到以下文件并解压 apktool——可以反编译软件的布局文件、图片等资源,方便大家学习一些很好的布局; https://bitbucket.org/iBotPeaches/apktool/downloads/ dex2jar——将apk反编译成java源码(classes.dex转化成jar文件); http://sourceforge.n…...

    2024/5/2 13:01:36
  8. SpringMVC开发——实现第一个RESTful接口

    SpringMVC最主要的一个功能就是设计接口,并提供给其他应用程序访问,如前端客户端等。RESTful接口是一种接口设计风格,也是一种设计规范,目前在项目开发中已经越来越流行。比如RESTful建议请求需要区分GET、POST、PUT等;返回的数据建议是JSON;网络协议使用https;请求url包…...

    2024/4/18 15:36:21
  9. OpenAI gym 自定义环境注册方法

    OpenAI gym 自定义环境注册方法方法写好myenv.py放置注册 因为要自己搭一个机器人环境,要借鉴一下Fetch-PickAndPlace-v1的环境,在它的基础上改进,所以就用它来注册一个新的环境来改。 方法 写好myenv.py 这里我直接复制粘贴一下pick_and_place.py,然后把类的名字改成MyEnv…...

    2024/4/26 0:50:15
  10. Eclipse插件开发 添加外部jar包

    RCP,SWT,插件开发【qq群】336280109 如果是直接jar包右键build path 进去的话,运行插件工程时候就会报错找不到类java.lang.NoClassDefFoundError 这时候需要在插件配置页面编辑plugin.xml中runtime页面的右下角classpath中添加过来jar包...

    2024/4/27 0:01:43
  11. select函数详细用法解析

    1.表头文件 #include #include #include 2.函数原型 int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout); 3.函数说明 select()用来等待文件描述词状态的改变。参数n代表最大的文件描述词加1,参数readfds、writefds和e…...

    2024/4/17 1:29:05
  12. Leetcode 997.找到小镇的法官

    题目详情解题思路 图类问题首先考虑出入度 建立两个int[] 一个入度一个出度 找入度=N-1和出度=0的位置 但是题目给的点都从1开始 而不是按照数组特征从0开始 所以要注意 代码实现 public class Solution {public int FindJudge(int N, int[][] trust) {int[] in_degree = new i…...

    2024/4/26 11:03:33
  13. RESTful简单介绍及示例

    一、什么是RESTfulRESTful 不是一种技术,也不是一种规范,而是一种架构(设计)风格。参考文章:什么是RESTREST(Representational State Transfer)表述性状态转移。7个HTTP方法:GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONSRESTful(Representational State Transfer)架构风格…...

    2024/4/26 22:58:26
  14. Logstatsh数据同步

    LogstashLogstash是elastic技术栈中的一个技术。它是一个数据采集引擎,可以从数据库采集数据到es中。我们可以通过设置自增id主键或者时间来控制数据的自动同步,这个id或者时间就是用于给logstash进行识别的id:假设现在有1000条数据,Logstatsh识别后会进行一次同步,同步完…...

    2024/4/25 23:34:54
  15. C#程序反编译教程

    工具: 1. de4dot - 脱壳工具 2. NET.Reflector Pro - C#IL源码查看器 流程: 1. 查看是否加壳 用NET.Reflector Pro打开程序, 如下图, 第一个红框是[傲视天下]的更新程序, 可以看出没加壳, 第二个红框是[有好米域名注册]的主程序, 加了壳的2. 脱壳, [可选(有壳才脱)]进入de4dot…...

    2024/4/26 5:17:01
  16. 程序员面试金典(LeetCode)-面试题02.05(链表求和)

    链表求和 题目描述算法思想 解决问题,我的方法是,将其中的值都取出来,然后转换为字符串,再转换为整数,然后相加后,再转换为列表,然后再构建链表放进去。但我在题解中看到一位大佬是直接通过进位记录的方式实现,只用了一次循环就完成了,代码在后。 代码实现 # Definiti…...

    2024/4/30 15:18:07
  17. 【Restful API】如何编写相对标准的后端项目--设计 Restful API

    [转自koala bear]本文主要介绍 Restful 风格,如何设计 HTTP Restful API,以及在设计过程中的一点经验之谈。文章有点枯燥,敬请谅解。最后建议在设计 HTTP API 时,遵循 Restful API 风格。理解 Restful Style在 web 飞速发展的环境下,Roy Fielding 于 2000 年在博士论文 Ar…...

    2024/4/30 5:30:16
  18. fastAdmin的插件cms

    CMS内容管理插件标签文档https://forum.fastadmin.net/thread/2580...

    2024/4/28 1:20:32
  19. [学习笔记]FWT——快速沃尔什变换

    解决涉及子集配凑的卷积问题 一、介绍 1.基本用法 FWT快速沃尔什变换学习笔记 就是解决一类问题:$f[k]=\sum_{i\oplus j=k}a[i]*b[j]$基本思想和FFT类似。首先转化成为另一个多项式$FWT(A),FWT(B)$使得:$FWT(A\oplus B)=FWT(A)FWT(B)$这里,$$是按位乘。这个是$O(n)$的。然后…...

    2024/5/2 6:03:01
  20. PB函数大全(超全,解析很全面)

    这几天在pb花的时间比较长,用一个新的ide,一个新的语言,在短时间内去开发一个MIS,也是比较有难度的一件事儿。最主要的是熟悉语法式和各种常用函数,再结合以前所学的编程语言,来快速消化并掌握它。 工欲善其事,必先利其器。所以熟悉了语法式后,常用函数则需要有个…...

    2024/5/2 4:09:17

最新文章

  1. springboot 自动配置源码解读

    什么是自动装配 当我们程序依赖第三方功能组件时&#xff0c;不需要手动将这些组件类加载到IOC容器中。例如 当程序需要用到redis时&#xff0c;在pom.xml文件中引入依赖&#xff0c;然后使用依赖注入的方式直接从IOC容器中拿到相应RedisTemplate实例。 SpringBootApplication …...

    2024/5/3 1:29:45
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. 【opencv】示例-ela.cpp JPEG图像的错误等级分析(ELA) 通过分析图像压缩后的差异来检测图像是否被篡改过...

    ela_modified.jpg 原始ela_modified压缩后再解压得到compressed_img 差异图像Ela 这段代码的功能是实现JPEG图像的错误等级分析&#xff08;ELA&#xff09;&#xff0c;通过分析图像压缩后的差异来检测图像是否被篡改过。程序会首先读取一张图片&#xff0c;然后对其应用质量…...

    2024/5/1 3:12:59
  4. 算法四十天-删除排序链表中的重复元素

    删除排序链表中的重复元素 题目要求 解题思路 一次遍历 由于给定的链表是排好序的&#xff0c;因此重复的元素在链表中的出现的位置是连续的&#xff0c;因此我们只需要对链表进行一次遍历&#xff0c;就可以删除重复的元素。 具体地&#xff0c;我们从指针cur指向链表的头节…...

    2024/5/1 13:07:13
  5. 在 Visual Studio Code (VSCode) 中隐藏以 . 开头的文件

    打开VSCode。 按下Ctrl ,快捷键打开设置。您也可以点击屏幕左下角的齿轮图标&#xff0c;然后选择“Settings”。 在设置搜索框中&#xff0c;键入files.exclude。 在找到的Files: Exclude项中&#xff0c;点击Add Pattern按钮来添加一个新的模式&#xff0c;或者直接在搜索…...

    2024/5/2 2:37:00
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/1 17:30:59
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/2 16:16:39
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/29 2:29:43
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/2 9:28:15
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/30 9:43:09
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/2 15:04:34
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/29 20:46:55
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/30 22:21:04
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/1 4:32:01
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/30 9:42:22
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/2 9:07:46
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/30 9:42:49
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57