当前位置: 首页 > 编程日记 > 正文

springboot过滤器排除掉一些url_理解这9大内置过滤器,才算是精通Shiro

小Hub领读:

权限框架一般都是一堆过滤器、拦截器的组合运用,在shiro中,有多少个内置的过滤器你知道吗?在哪些场景用那些过滤器,这篇文章希望你能对shiro有个新的认识!

别忘了,点个 [在看] 支持一下哈~


前两篇原创shiro相关文章:

1、极简入门,Shiro的认证与授权流程解析

2、只需要6个步骤,springboot集成shiro,并完成登录


我们都知道shiro是个认证权限框架,除了登录、退出逻辑我们需要侵入项目代码之外,验证用户是否已经登录、是否拥有权限的代码其实都是过滤器来完成的,可以这么说,shiro其实就是一个过滤器链集合。

那么今天我们详细讨论一下shiro底层到底给我们提供了多少默认的过滤器供我们使用,又都有什么用呢?带着问题,我们先去shiro官网看看对于默认过滤器集的说明。

  • http://shiro.apache.org/web.html#default-filters
When running a web-app, Shiro will create some useful default Filter instances and make them available in the [main] section automatically. You can configure them in main as you would any other bean and reference them in your chain definitions.
The default Filter instances available automatically are defined by the DefaultFilter enum and the enum’s name field is the name available for configuration.

翻译过来意思:

当运行web应用程序时,Shiro将创建一些有用的默认过滤器实例,并使它们在[main]部分自动可用。您可以像配置任何其他bean一样在main中配置它们,并在链定义中引用它们。

默认筛选器实例由DefaultFilter enum中定义,enum s name字段是可用于配置的名称。

于是我看了一下DefaultFilter的源码:

public enum DefaultFilter {anon(AnonymousFilter.class),authc(FormAuthenticationFilter.class),authcBasic(BasicHttpAuthenticationFilter.class),logout(LogoutFilter.class),noSessionCreation(NoSessionCreationFilter.class),perms(PermissionsAuthorizationFilter.class),port(PortFilter.class),rest(HttpMethodPermissionFilter.class),roles(RolesAuthorizationFilter.class),ssl(SslFilter.class),user(UserFilter.class);...
}

终于知道我们常用的anon、authc、perms、roles、user过滤器是哪里来的了!这些过滤器我们都是可以直接使用的。但你要弄清楚这些默认过滤器,你还不得不去深入了解一下shiro更底层为我们提供的过滤器,基本我们的这些默认过滤器都是通过继承这几个底层过滤器演变而来的。

那么这些过滤器都有哪些呢?我们来看一个图:

5aa5fc8b534cb8228a69a09ee937c383.png

上面我标记了7个我们接下来要介绍的过滤器,我们一个个来介绍,弄清楚这些过滤器之后,相信你对shiro的认识会更深一层了。具体authc、perms、roles等这些默认过滤器与这7个过滤器有什么关系你就会明白。

1、AbstractFilter

这个过滤器还得说说,shiro最底层的抽象过滤器,虽然我们极少直接继承它,它通过实现Filter获得过滤器的特性。

7864fb963c2674bd4e880d431b40316e.png

完成一些过滤器基本初始化操作,FilterConfig:过滤器配置对象,用于servlet容器在初始化期间将信息传递给其他过滤器。

2、NameableFilter

命名过滤器,给过滤器定义名称!也是比较基层的过滤器了,未拓展其他功能,我们很少会直接继承这个过滤器。为重写doFilter方法。

f62ffc3c9241c0e13714ec88034ec460.png

3、OncePerRequestFilter

9a31b415c2ffe1946d28ca5a7d4bccc9.png

重写doFilter方法,保证每个servlet方法只会被过滤一次。可以看到doFilter方法中,第一行代码就是String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();然后通过request.getAttribute(alreadyFilteredAttributeName) != null来判断过滤器是否已经被调用过,从而保证过滤器不会被重复调用。

进入方法之前,先标记alreadyFilteredAttributeName为True,抽象doFilterInternal方法执行之后再remove掉alreadyFilteredAttributeName

b5123dc9d823c221e3d4bd33d61fd9a1.png

所以OncePerRequestFilter过滤器保证只会被一次调用的功能,提供了抽象方法doFilterInternal让后面的过滤器可以重写,执行真正的过滤器处理逻辑。

protected abstract void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)throws ServletException, IOException;

这个过滤器我们已经可以开始在我们的项目继承使用,比如拦截用户请求,判断用户是否已经登录(携带token或cookie信息),如果未登录则返回Json数据告知未登录!

比如: 开源mblog博客项目中,过滤器就是继承OncePerRequestFilter。

  • https://gitee.com/mtons/mblog
/*** 公众号:MarkerHub
**/
public class AuthenticatedFilter extends OncePerRequestFilter {// 前端弹窗的js代码private static final String JS = "<script type='text/javascript'>var wp=window.parent; if(wp!=null){while(wp.parent&&wp.parent!==wp){wp=wp.parent;}wp.location.href='%1$s';}else{window.location.href='%1$s';}</script>";private String loginUrl = "/login";// 重写doFilterInternal方法@Overrideprotected void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)throws ServletException, IOException {Subject subject = SecurityUtils.getSubject();// 已经登陆就跳过过滤器if (subject.isAuthenticated() || subject.isRemembered()) {chain.doFilter(request, response);} else {// 未登录就返回json或者js代码WebUtils.saveRequest(request);String path = WebUtils.getContextPath((HttpServletRequest) request);String url = loginUrl;if (StringUtils.isNotBlank(path) && path.length() > 1) {url = path + url;}if (isAjaxRequest((HttpServletRequest) request)) {response.setContentType("application/json;charset=UTF-8");response.getWriter().print(JSON.toJSONString(Result.failure("您还没有登录!")));} else {response.getWriter().write(new Formatter().format(JS, url).toString());}}}
}

未登录情况,ajax请求过滤器返回您还没有登录!提示,web请求则返回一段js代码,前端渲染会跳出一个登陆窗口,这也就是未什么大家常遇到的点击登录,当前跳出一个登陆弹窗的一种实现方式!

效果:

f3d7c8d806ec234e121139a9afd19ad6.png

4、AdviceFilter

看到Advice,很自然想到切面环绕编程,一般有pre、post、after几个方法。所以这个AdviceFilter过滤器就是提供了和AOP相似的切面功能。

继承OncePerRequestFilter过滤器重写doFilterInternal方法,我们可以先看看:

86201351d700f59ae96e0cf2017ce974.png

可以看到上面4个序号:

  • 1、preHandle 前置过滤,默认true
  • 2、executeChain 执行真正代码过滤逻辑->chain.doFilter
  • 3、postHandle 后置过滤
  • 4、cleanup 其实主要逻辑是afterCompletion方法

于是,我们从OncePerRequestFilter的一个doFilterInternal分化成了切面编程,更容易前后控制执行逻辑。所以如果继承AdviceFilter时候,我们可以重写preHandle方法,判断用户是否满足已登录或者其他业务逻辑,返回false时候表示不通过过滤器。

5、PathMatchingFilter

请求路径匹配过滤器,通过匹配请求url,判断请求是否需要过滤,如果url未在需要过滤的集合内,则跳过,否则进入isFilterChainContinued的onPreHandle方法。

我们可以看下代码:

e557e7b98e868087334f502753ad37c6.png

1013116f11cb69c51516bcf801e317e4.png

从上面3个步骤中可以看到,PathMatchingFilter提供的功能是:自定义匹配url,匹配上的请求最终跳转到onPreHandle方法。

这个过滤器为后面的常用过滤器提供的基础,比如我们在config中配置如下

/login = anon
/admin/* = authc

拦截/login请求,经过AnonymousFilter过滤器,我们可以看下

  • org.apache.shiro.web.filter.authc.AnonymousFilter
public class AnonymousFilter extends PathMatchingFilter {/*** 公众号:MarkerHub**/@Overrideprotected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) {// Always return true since we allow access to anyonereturn true;}
}

AnonymousFilter重写了onPreHandle方法,只不过直接返回了true,说明拦截的链接可以直接通过,不需要其他拦截逻辑。

而authc->FormAuthenticationFilter也是间接继承了PathMatchingFilter。

public class FormAuthenticationFilter extends AuthenticatingFilter

所以,需要拦截某个链接进行业务逻辑过滤的可以继承PathMatchingFilter方法拓展哈。

6、AccessControlFilter

访问控制过滤器。继承PathMatchingFilter过滤器,重写onPreHandle方法,又分出了两个抽象方法来控制

2282fe5e7745caa045f68f55c1a98554.png
  • isAccessAllowed 是否允许访问
  • onAccessDenied 是否拒绝访问

所以,我们现在可以通过重写这个抽象两个方法来控制过滤逻辑。另外多提供了3个方法,方便后面的过滤器使用。

/*** 公众号:MarkerHub
**/
protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {saveRequest(request);redirectToLogin(request, response);
}protected void saveRequest(ServletRequest request) {WebUtils.saveRequest(request);
}protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {String loginUrl = getLoginUrl();WebUtils.issueRedirect(request, response, loginUrl);
}

其中redirectToLogin提供了调整到登录页面的逻辑与实现,为后面的过滤器发现未登录跳转到登录页面提供了基础。

这个过滤器,我们可以灵活运用。

7、AuthenticationFilter

继承AccessControlFilter,重写了isAccessAllowed方法,通过判断用户是否已经完成登录来判断用户是否允许继续后面的逻辑判断。这里可以看出,从这个过滤器开始,后续的判断会与用户的登录状态相关,直接继承这些过滤器,我们不需要再自己手动去判断用户是否已经登录。并且提供了登录成功之后跳转的方法。

public abstract class AuthenticationFilter extends AccessControlFilter {public void setSuccessUrl(String successUrl) {this.successUrl = successUrl;}protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {Subject subject = getSubject(request, response);return subject.isAuthenticated();}
}

8、AuthenticatingFilter

继承AuthenticationFilter,提供了自动登录、是否登录请求等方法。

/*** 公众号:MarkerHub
**/
public abstract class AuthenticatingFilter extends AuthenticationFilter {public static final String PERMISSIVE = "permissive";//TODO - complete JavaDocprotected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {AuthenticationToken token = createToken(request, response);if (token == null) {String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +"must be created in order to execute a login attempt.";throw new IllegalStateException(msg);}try {Subject subject = getSubject(request, response);subject.login(token);return onLoginSuccess(token, subject, request, response);} catch (AuthenticationException e) {return onLoginFailure(token, e, request, response);}}protected abstract AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception;/*** 公众号:MarkerHub**/@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {return super.isAccessAllowed(request, response, mappedValue) ||(!isLoginRequest(request, response) && isPermissive(mappedValue));}...
}
  • executeLogin 执行登录
  • onLoginSuccess 登录成功跳转
  • onLoginFailure 登录失败跳转
  • createToken 创建登录的身份token
  • isAccessAllowed 是否允许被访问
  • isLoginRequest 是否登录请求

这个方法提供了自动登录的课程,比如我们获取到token之后实行自动登录,这场景还是很场景的。

比如在开源项目renren-fast中,就是这样处理的:

  • https://gitee.com/renrenio/renren-fast
public class OAuth2Filter extends AuthenticatingFilter {@Overrideprotected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {//获取请求tokenString token = getRequestToken((HttpServletRequest) request);if(StringUtils.isBlank(token)){return null;}return new OAuth2Token(token);}@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {if(((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())){return true;}return false;}@Overrideprotected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {//获取请求token,如果token不存在,直接返回401String token = getRequestToken((HttpServletRequest) request);if(StringUtils.isBlank(token)){HttpServletResponse httpResponse = (HttpServletResponse) response;httpResponse.setHeader("Access-Control-Allow-Credentials", "true");httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());String json = new Gson().toJson(R.error(HttpStatus.SC_UNAUTHORIZED, "invalid token"));httpResponse.getWriter().print(json);return false;}return executeLogin(request, response);}/***公众号:MarkerHub
**/@Overrideprotected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {HttpServletResponse httpResponse = (HttpServletResponse) response;httpResponse.setContentType("application/json;charset=utf-8");httpResponse.setHeader("Access-Control-Allow-Credentials", "true");httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());try {//处理登录失败的异常Throwable throwable = e.getCause() == null ? e : e.getCause();R r = R.error(HttpStatus.SC_UNAUTHORIZED, throwable.getMessage());String json = new Gson().toJson(r);httpResponse.getWriter().print(json);} catch (IOException e1) {}return false;}/*** 获取请求的token*/private String getRequestToken(HttpServletRequest httpRequest){//从header中获取tokenString token = httpRequest.getHeader("token");//如果header中不存在token,则从参数中获取tokenif(StringUtils.isBlank(token)){token = httpRequest.getParameter("token");}return token;}}

onAccessDenied方法校验通过之后执行executeLogin方法完成自动登录!

9、FormAuthenticationFilter

基于form表单的账号密码自动登录的过滤器,我们只需要看这个方法就明白,和renren-fast的实现相似:

public class FormAuthenticationFilter extends AuthenticatingFilter {public static final String DEFAULT_USERNAME_PARAM = "username";public static final String DEFAULT_PASSWORD_PARAM = "password";public static final String DEFAULT_REMEMBER_ME_PARAM = "rememberMe";protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {String username = getUsername(request);String password = getPassword(request);return createToken(username, password, request, response);}/*** 公众号:MarkerHub**/protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {if (isLoginRequest(request, response)) {if (isLoginSubmission(request, response)) {if (log.isTraceEnabled()) {log.trace("Login submission detected.  Attempting to execute login.");}return executeLogin(request, response);} else {if (log.isTraceEnabled()) {log.trace("Login page view.");}//allow them to see the login page ;)return true;}} else {if (log.isTraceEnabled()) {log.trace("Attempting to access a path which requires authentication.  Forwarding to the " +"Authentication url [" + getLoginUrl() + "]");}saveRequestAndRedirectToLogin(request, response);return false;}}
}

onAccessDenied调用executeLogin方法。默认的token是UsernamepasswordToken。

结束语

好了,今天先到这里啦,讲了多好内置的过滤器,代码有点多,你们可以用电脑打开文章,然后仔细研究,并回想自己使用shiro过滤器的时候,是不是和我讲的场景一样,结合起来。

这里是MarkerHub,我是吕一明,感谢关注与支持!

相关文章:

安装APK,启动系统Activity

要同时设置data和type的话只能用函数setDataAndType private void installApk(File file) {Intent intent new Intent("android.intent.action.VIEW");intent.addCategory("android.intent.category.DEFAULT"); // intent.setData(Uri.fromFile(fi…

EOS能不能囤?一篇文章搞懂EOS优缺点

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 EOS是一个基于区块链的开发平台&#xff0c;专为构建去中心化应用程序&#xff08;dApp&#xff09;而设计。EOS是一个开源项目&#xff0c;其源代…

JS 中的事件设计

看懂此文&#xff0c;不再困惑于 JS 中的事件设计 原文出处&#xff1a; aitangyong 抽空学习了下javascript和jquery的事件设计&#xff0c;收获颇大&#xff0c;总结此贴&#xff0c;和大家分享。 (一)事件绑定的几种方式 javascript给DOM绑定事件处理函数总的来说有2种方式…

‘百度杯’十月场web ---login

首先一看的题&#xff0c;既然是是web类的&#xff0c;就要查看源码&#xff0c;一看&#xff0c;最先有一行注释&#xff0c;估摸着是用户名和密码 果然登录上去了&#xff0c;显示一段乱码&#xff0c;源码也没有什么东西&#xff0c; 那就抓一次包吧 发现响应头里边有个sho…

oracle 与 client端执行结果不一致_不同模式下Spark应用的执行过程

根据应用执行的3个阶段&#xff0c;不同执行模式下各个阶段的执行逻辑不相同&#xff0c;本文分析不同模式下的执行逻辑。Yarn-Client模式的执行流程Yarn的组成Yarn是hadoop自带的资源管理框架&#xff0c;它的设计思想是&#xff1a;YARN的基本思想是将资源管理和作业调度/监视…

分享EOS加拿大的文章《REX——从源代码做技术解析》

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 已提议将丢失密钥恢复系统部署到EOS主网。 丢失密钥解决方案的最后一步已经在链上提出。最后一步是将丢失密钥解决方案智能合约部署到为此目的创建…

16.QT鼠标

头文件 1 #include <QMouseEvent> 2 #include <QStatusBar> 3 #include <QLabel> 1 protected: 2 //鼠标按下 3 void mousePressEvent(QMouseEvent *e); 4 //鼠标移动 5 void mouseMoveEvent(QMouseEvent *e); 6 //鼠标释放 7 void …

c++ windows获得当前工作目录文件_基于linux下Python文件操作

Python中的文件操作1、文件的打开与关闭想一想&#xff1a;如果想用word编写一份简历&#xff0c;应该有哪些流程呢&#xff1f;1、打开word软件&#xff0c;新建一个word文件2、写入个人简历信息3、保存文件4、关闭word软件同样&#xff0c;在操作文件的整体过程与使用word编写…

maven 插件

maven-enforcer-plugin https://maven.apache.org/enforcer/maven-enforcer-plugin/ https://maven.apache.org/enforcer/maven-enforcer-plugin/enforce-mojo.html http://maven.apache.org/enforcer/enforcer-rules/index.html转载于:https://www.cnblogs.com/SamuelSun/p/58…

数字货币EOS半年时间暴跌90%多,还可追捧吗?

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 对财富上的自由&#xff0c;很多人认为这比感情来得容易多了。但是面对这个竞争激烈的社会&#xff0c;是兢兢业业的工作&#xff0c;还是投资房地产…

resnet keras 结构_Wandb用起来,一行Python代码实现Keras模型可视化

大数据文摘出品来源&#xff1a;wandb编译&#xff1a;邢畅、宁静在训练神经网络的过程中&#xff0c;我们可能会希望可视化网络的性能和中间的结构&#xff0c;很多可视化代码的冗长复杂使得我们望而却步&#xff0c;有没有一行代码就能解决可视化的所有问题呢&#xff1f;通过…

tensorflow学习笔记————分类MNIST数据集

在使用tensorflow分类MNIST数据集中&#xff0c;最容易遇到的问题是下载MNIST样本的问题。 一般是通过使用tensorflow内置的函数进行下载和加载&#xff0c; from tensorflow.examples.tutorials.mnist import input_data mnist input_data.read_data_sets("MNIST_data&q…

Build SSCLI20 under VS2008 full Document (完全手册)

以前build过几次sscli2都成功了&#xff0c;这次换了个新的环境&#xff0c;没想到出了一大堆的问题。折腾了半天&#xff0c;最终搞定&#xff0c;把解决问题的过程和方法都记录下来。 首先说说build的过程中参考过的链接和资源。 首先就是sscli自带的文档&#xff1a;Buildin…

vue 发展历程时间轴动画_PPT时间轴如何做出创意感?海量素材免费分享,网友:收藏...

时间轴页面&#xff0c;是工作型PPT中常见的页面之一。个人述职或者公司介绍PPT中&#xff0c;使用时间轴&#xff0c;能够让观众更加清晰地了解公司的发展历程。但是&#xff0c;很多人在制作时间轴页面时&#xff0c;往往是这样的效果&#xff1a;只有几行字和一根线&#xf…

[CQOI2014]数三角形 组合数 + 容斥 + gcd

推导过程 &#xff1a; 组合数容斥原理gcd 正确做法是暴力的一种优化&#xff0c;ans所有情况 - 平行坐标轴的三点共线 - 斜线三点共线 如果快速求斜线三点共线&#xff1a; 首先要知道一个结论&#xff0c;对于点(a,b) (x,y)连成的线段而言(其中a>x,b>y)&#xff0c; 在…

石子合并[DP-N3]

题目描述 在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆&#xff0c;并将新的一堆的石子数&#xff0c;记为该次合并的得分。 试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分. 输入输出格式 输入格式&…

智能合约智能么?

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 近几年&#xff0c;随着区块链、加密货币概念的发展&#xff0c;智能合约也开始被广泛的接受&#xff0c;然而就像最初的人工智能被过度神化一样&…

Codeforces 504 A (Round #285 div.1 A) Misha and Forest

Codeforces Round #285 (Div.1) A Misha and Forest 水题水题水…… 题意&#xff1a;给你一些点&#xff0c;给出他们连通了多少个点以及这些点的下标的异或值&#xff0c;让你找出一个图 题解&#xff1a;拓扑排序一发 代码: #include <iostream> #include <cstdio&…

hashlib模式和hmac模式

hashlib模式 什么叫hash&#xff1f; 一&#xff1a;hash是一种算法&#xff08;3.x里代替了md5模块和sha模块&#xff0c;主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 &#xff0c;MD5 算法&#xff09;&#xff0c;该算法接受传入的内容&#xff0c;经过运算得到一串hash…

asp导出word中文乱码_解决文档打开乱码问题丨小工具系列

问题:手头上有个从Workbench导出的数据表文档打开发现里面的中文是乱码&#xff01;如图所示&#xff1a;解决方法利用记事本&#xff08;notepad&#xff09;将该文档的格式修改为UTF-8&#xff0c;步骤如下点击电脑的开始菜单&#xff0c;点击"所有程序"&#xff0…

一篇文章让你了解智能合约以及和区块链的关系

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 智能合约是区块链最重要的特性&#xff0c;也是区块链能够被称为颠覆性技术的主要原因&#xff0c;更是各国央行考虑使用区块链技术来发行数字货币的…

我的常用npm命令

npm link gulp node-sass gulp-sass gulp-autoprefixer gulp-sourcemaps gulp-font-spider gulp-concat gulp-uglify gulp-jshint map-stream 转载于:https://www.cnblogs.com/siluo2000/p/8779988.html

pytorch 测试每一类_DeepFM全方面解析(附pytorch源码)

写在前面最近看了DeepFM这个模型。把我学习的思路和总结放上来给大家和未来的自己做个参考和借鉴。文章主要希望能串起学习DeepFM的各个环节&#xff0c;梳理整个学习思路。以“我”的角度浅谈一下DeepFM基础知识看过的一些有用文献最后附上可实现的pytorch代码&#xff0c;用具…

超简单的网页选项卡---jQuery

<!DOCTYPE html><html lang"en"><head> <meta charset"UTF-8"> <title>网页选项卡</title> <script src"jquery-1.4.2.js"></script> <script type"text/javascript"> $(funct…

一篇文章让你了解区块链技术的发展阶段

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 区块链是由一系列技术实现的全新去中心化经济组织模式&#xff0c;2009年诞生于比特币系统的构建&#xff0c;2017年成为全球经济热点&#xff0c;但…

301 Remove Invalid Parentheses 删除无效的括号

删除最小数目的无效括号&#xff0c;使输入的字符串有效&#xff0c;返回所有可能的结果。注意: 输入可能包含了除 ( 和 ) 以外的元素。示例 :"()())()" -> ["()()()", "(())()"]"(a)())()" -> ["(a)()()", "(a(…

python3 列表转字节_Python 3.9!10大新特性值得关注

选自towardsdatascience作者&#xff1a;Farhad Malik机器之心编译编辑&#xff1a;陈萍近日&#xff0c;Python 3.9 发布&#xff0c;并开发了一些新特性&#xff0c;包括字典合并与更新、新的解析器、新的字符串函数等。Python 3.9 已于 10 月 5 日发布&#xff0c;新版本的特…

HDU4080 Stammering Aliens(二分 + 后缀数组)

题目 Source http://acm.hdu.edu.cn/showproblem.php?pid4080 Description Dr. Ellie Arroway has established contact with an extraterrestrial civilization. However, all efforts to decode their messages have failed so far because, as luck would have it, they ha…

共识机制:区块链技术的根基

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 Chapter&#xff0d;1&#xff1a;什么是共识机制&#xff1f; 技术定义是&#xff1a;共识机制是一个群体决策的流程&#xff0c;群体中的个体会执…

Web App、Hybrid App与Native App的设计差异

目前主流应用程序大体分为三类&#xff1a;Web App、Hybrid App、 Native App。 一、Web App、Hybrid App、Native App 纵向对比 首先&#xff0c;我们来看看什么是 Web App、Hybrid App、 Native App。 1. Web APP Web App 指采用Html5语言写出的App&#xff0c;不需要下载安装…