apache shiro_Apache Shiro第2部分–领域,数据库和PGP证书

apache shiro

这是致力于Apache Shiro的系列文章的第二部分。 我们从简单的不安全Web应用程序开始了上一部分 。 完成后,该应用程序具有基本的身份验证和授权。 用户可以登录和注销。 所有网页和按钮均已分配并实施了访问权限。 授权和身份验证数据都已存储在静态配置文件中。

正如我们在最后一部分中承诺的,我们将用户帐户数据移至数据库。 另外,我们将为用户提供通过PGP证书进行身份验证的选项。 因此,我们的应用程序将具有多个备用登录选项:使用用户名/密码登录和使用证书登录。 最后,我们将强制启用备用登录选项。

换句话说,我们将展示如何创建自定义领域以及如何处理多领域方案。 我们将创建三个不同版本的SimpleShiroSecuredApplication:

  • 版本,并将所有帐户信息移至数据库 ,
  • 允许PGP证书作为替代身份验证机制的版本 ,
  • 需要同时输入用户名/密码和PGP证书的版本 。

每个版本都有测试类RunWaitTest。 该类使用在http:// localhost:9180 / simpleshirosecuredapplication / url上部署的应用程序启动Web服务器。

注意:自第一版以来,我们更新了上一部分。 最显着的变化是新部分 ,该部分显示了如何向登录页面添加错误消息。 感谢大家的反馈。

境界

首先,我们解释什么是领域以及如何创建它们。 如果您对理论不感兴趣,请继续下一章 。
领域负责身份验证和授权。 每当用户想要登录到应用程序时,都会收集身份验证信息并将其传递到领域。 Realm验证提供的数据并决定是否应允许用户登录,访问资源或拥有特定角色。
认证信息包括两个部分:

  • 主体–代表帐户唯一标识符,例如用户名,帐户ID,PGP证书等。
  • 凭证–证明用户身份,例如密码,PGP证书,指纹等。

Shiro提供了能够从活动目录 , ldap , ini文件 , 属性文件和数据库中读取授权数据的领域。 在Shiro.ini文件的主要部分中配置领域:

realmName=org.apache.shiro.realm.jdbc.JdbcRealm

认证方式

所有领域都实现Realm接口。 有两种重要的接口方法:supports和getAuthenticationInfo。 两者都在身份验证令牌对象中接收主体和凭据。
支持方法根据提供的身份验证令牌确定领域是否能够对用户进行身份验证。 例如,如果我的领域检查用户名和密码,则仅使用X509证书拒绝身份验证令牌。
方法getAuthenticationInfo本身执行身份验证。 如果来自身份验证令牌的主体和凭据表示有效的登录信息,则该方法返回身份验证信息对象。 否则,领域返回null。

授权书

如果该领域还希望进行授权,则必须实现Authorizer接口。 每个Authorizer方法都将主体作为参数,并检查角色或权限。 重要的是要理解,该领域会获得所有授权请求,即使它们来自另一个领域进行了身份验证的用户也是如此。 当然,领域可以决定忽略任何授权请求。
权限以字符串或权限对象的形式提供。 除非有充分的理由,否则请使用WildcardPermissionResolver将字符串转换为权限对象。

其他选择

Shiro框架在运行时调查其他接口的领域。 如果领域实现了它们,则可以使用:

  • 有关用户注销的信息,
  • 有关系统启动的信息,
  • 全局缓存
  • 在配置文件中配置的名称 ,
  • 在权限字符串和权限对象之间配置的转换器 。

这些功能可用于实现其他接口的任何领域。 无需其他配置。
自定义领域

创建新领域的最简单方法是扩展AuthenticatingRealm或AuthorizingRealm类。 它们具有上一节中提到的所有有用接口的合理实现。 如果它们不能满足您的需求,则可以扩展CachingRealm或从头开始创建新领域。

移至数据库

当前版本的SimpleShiroSecuredApplication使用默认领域进行身份验证和授权。 默认领域– IniRealm从配置文件读取用户帐户信息。 这种存储仅对于最简单的应用程序是可接受的。 任何稍微复杂的事情都需要将凭据存储在更好的持久性存储中。
新要求:帐户凭据和访问权限存储在数据库中。 存储的密码经过哈希处理和加盐处理。
在本章中,我们将把应用程序连接到数据库并创建表以存储所有用户帐户数据。 然后,我们将IniRealm替换为能够从数据库和salt密码读取的领域。

数据库基础架构

本节介绍示例应用程序基础结构。 它不包含有关Shiro的信息,因此您可以自由地跳过它 。
示例应用程序以嵌入式模式使用Apache Derby数据库。
我们使用Liquibase进行数据库部署和升级。 它是开源库,用于跟踪,管理和应用数据库更改。 数据库更改(新表,新列,外键)存储在数据库更改日志文件中。 启动后,Liquibase会调查数据库并应用所有新更改。 结果,数据库始终保持一致并且是最新的,而我们却没有付出任何努力。 将对Derby和Liquibase的依赖项添加到SimpleShiroSecuredApplication pom.xml中 :

<dependency><groupid>org.apache.derby</groupid><artifactid>derby</artifactid><version>10.7.1.1</version>
</dependency>
<dependency><groupid>org.liquibase</groupid><artifactid>liquibase-core</artifactid><version>2.0.1</version>
</dependency>

将jndi添加到码头:

<dependency><groupid>org.mortbay.jetty</groupid><artifactid>jetty-naming</artifactid><version>${jetty.version}</version><scope>test</scope>
</dependency>  
<dependency><groupid>org.mortbay.jetty</groupid><artifactid>jetty-plus</artifactid><version>${jetty.version}</version><scope>test</scope>
</dependency>

使用数据库结构描述创建db.changelog.xml文件。 它创建用于存储用户,角色和权限的表。 它还用初始数据填充这些表。 我们使用random_salt_value_username作为盐,并使用以下方法创建哈希盐化密码:

public static String simpleSaltedHash(String username, String password) {Sha256Hash sha256Hash = new Sha256Hash(password, (new SimpleByteSource('random_salt_value_' + username)).getBytes());String result = sha256Hash.toHex();System.out.println(username + ' simple salted hash: ' + result);return result;
}

在WEB-INF / jetty-web.xml文件中创建指向derby的数据源:

<configure class='org.mortbay.jetty.webapp.WebAppContext' id='SimpleShiroSecuredApplication'><new class='org.mortbay.jetty.plus.naming.Resource' id='SimpleShiroSecuredApplication'><arg>jdbc/SimpleShiroSecuredApplicationDB</arg><arg><new class='org.apache.derby.jdbc.EmbeddedDataSource'><set name='DatabaseName'>../SimpleShiroSecuredApplicationDatabase</set><set name='createDatabase'>create</set></new></arg></new>
</configure>

在web.xml文件中配置数据源和liquibase:

<resource-ref><description>Derby Connection</description><res-ref-name>jdbc/SimpleShiroSecuredApplicationDB</res-ref-name><res-type>javax.sql.DataSource</res-type><res-auth>Container</res-auth>
</resource-ref><context-param><param-name>liquibase.changelog</param-name><param-value>src/main/resources/db.changelog.xml</param-value>
</context-param><context-param><param-name>liquibase.datasource</param-name><param-value>jdbc/SimpleShiroSecuredApplicationDB</param-value>
</context-param><listener><listener-class>liquibase.integration.servlet.LiquibaseServletListener</listener-class>
</listener>

最后,在启用了jndi的情况下配置为读取jetty-web.xml的jetty在AbstractContainerTest类中。

创建新领域

Shiro提供的JDBCRealm能够执行身份验证和授权。 它使用可配置SQL查询从数据库中读取用户名,密码,权限和角色。 不幸的是,该领域有两个缺点:

  • 它无法从JNDI加载数据源( 未解决的问题 )。
  • 它无法添加密码( 未解决的问题 )。

我们对其进行扩展,并创建新的类JNDIAndSaltAwareJdbcRealm 。 由于所有属性都可以在ini文件中进行配置,因此新属性jndiDataSourceName也将自动进行配置。 只要设置了新属性,该领域就会在JNDI中查找数据源:

protected String jndiDataSourceName;public String getJndiDataSourceName() {return jndiDataSourceName;
}public void setJndiDataSourceName(String jndiDataSourceName) {this.jndiDataSourceName = jndiDataSourceName;this.dataSource = getDataSourceFromJNDI(jndiDataSourceName);
}private DataSource getDataSourceFromJNDI(String jndiDataSourceName) {try {InitialContext ic = new InitialContext();return (DataSource) ic.lookup(jndiDataSourceName);} catch (NamingException e) {log.error('JNDI error while retrieving ' + jndiDataSourceName, e);throw new AuthorizationException(e);}
}

方法doGetAuthenticationInfo从数据库读取帐户身份验证信息,并将其转换为身份验证信息对象。 如果找不到帐户信息,则返回null。 父类AuthenticatingRealm将身份验证信息对象与原始用户提供的数据进行比较。
我们重写doGetAuthenticationInfo以从数据库中读取密码哈希和盐,并将它们存储在身份验证信息对象中:

doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {...// read password hash and salt from db PasswdSalt passwdSalt = getPasswordForUser(username);...// return salted credentialsSimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, passwdSalt.password, getName());info.setCredentialsSalt(new SimpleByteSource(passwdSalt.salt));return info;
}

这里的示例仅包含最重要的代码段。 在Github上有完整课程 。

配置新领域

在Shiro.ini文件中配置领域和jndi名称:

[main] 
# realm to be used
saltedJdbcRealm=org.meri.simpleshirosecuredapplication.realm.JNDIAndSaltAwareJdbcRealm
# any object property is automatically configurable in Shiro.ini file
saltedJdbcRealm.jndiDataSourceName=jdbc/SimpleShiroSecuredApplicationDB 
# the realm should handle also authorization
saltedJdbcRealm.permissionsLookupEnabled=true

配置SQL查询:

# If not filled, subclasses of JdbcRealm assume 'select password from users where username = ?'
# first result column is password, second result column is salt 
saltedJdbcRealm.authenticationQuery = select password, salt from sec_users where name = ?
# If not filled, subclasses of JdbcRealm assume 'select role_name from user_roles where username = ?'
saltedJdbcRealm.userRolesQuery = select role_name from sec_users_roles where user_name = ?
# If not filled, subclasses of JdbcRealm assume 'select permission from roles_permissions where role_name = ?'
saltedJdbcRealm.permissionsQuery = select permission from sec_roles_permissions where role_name = ?

JdbcRealm使用credetials匹配器的方式与IniRealm完全相同:

# password hashing specification
sha256Matcher = org.apache.shiro.authc.credential.HashedCredentialsMatcher
sha256Matcher.hashAlgorithmName=SHA-256
saltedJdbcRealm.credentialsMatcher = $sha256Matcher

注意:我们从配置文件中删除了[用户]和[角色]部分。 否则,Shiro将同时使用IniRealm和JdbcRealm。 这将创建超出本章范围的多领域方案。

从用户的角度来看,应用程序的工作方式与以前完全相同。 他可以登录到以前相同的用户帐户。 但是,用户名,密码,盐,权限和角色现在存储在数据库中。

完整的源代码可在Github上的'authentication_stored_in_database'分支中找到。

备用登录–证书

一些系统允许用户登录使用多种身份验证方式。例如,用户可以提供用户名/密码,使用Google帐户,Facebook帐户或其他任何方式登录。 我们将添加与简单应用程序类似的内容。 我们将为用户提供使用PGP证书进行身份验证的选项。
新要求:应用程序支持PGP证书作为替代身份验证机制。 仅当用户不拥有与应用程序帐户关联的有效证书时,才会显示登录屏幕。 如果用户具有有效的已知PGP证书,则会自动登录。
用户尝试登录应用程序时,必须提供身份验证数据。 这些数据由servlet过滤器捕获。 过滤器将数据转换为身份验证令牌,并将令牌传递给领域。 如果任何领域都希望对用户进行身份验证,它将身份验证令牌转换为身份验证信息对象。 如果该领域不希望这样做,那么它将返回null。 开箱即用的Shiro框架过滤器会忽略请求中的PGP证书。 可用的身份验证令牌无法保存它们,并且领域完全不知道PGP证书。 因此,我们必须创建:

  • 身份验证令牌来移动证书,
  • Servlet过滤器能够读取证书,
  • 验证证书并将其与用户帐户匹配的领域。

我们的应用程序将有两个不同的领域。 一种使用名称标识帐户和密码来验证用户身份,另一种使用PGP证书两者都进行。
在开始编码之前,我们必须处理应用程序周围的PGP证书和基础结构。 如果您对设置的PGP证书不感兴趣,

基础设施

当用户访问Web应用程序时,他的Web浏览器可能会将PGP证书的副本发送到Web服务器。 证书由某个证书颁发机构或证书本身(自签名证书)签名。 Web服务器将其信任的证书列表保存在称为truststore的存储中。 如果信任库包含用户证书或对其进行签名的授权证书,则Web服务器将信任用户证书。 受信任的证书将传递到应用程序。
我们会:

  • 为每个用户创建证书,
  • 创建信任库,
  • 配置Web服务器,
  • 将证书与用户帐户关联。

在portecle中创建和管理证书。 SimpleShiroSecuredApplication的示例证书位于src \ test \ resources \ clients目录中。 所有商店和证书都具有通用密码“秘密”。

创建证书

为portecle中的每个用户创建自签名证书:

  • 创建新的jks密钥库:在File-> New Keystore中,选择jks。
  • 生成新证书:工具->生成密钥对。 将密码字段保留为空,证书将继承密钥库的密码。
  • 导出公共证书:选择新证书->右键单击->导出,选择“头证书”。 这将创建.cer文件。
  • 导出私钥和证书:选择新证书->右键单击->导出,选择私钥和证书。 这将创建.p12文件。

.cer文件仅包含公共证书,因此您可以将其提供给任何人。 另一方面,.p12文件包含用户私钥,因此必须保密。 仅将其分发给用户(例如,将其导入浏览器进行测试)。

创建信任库

创建新的信任库并将公共证书.cer文件导入到其中:

  • 在文件->新密钥库中,选择jks。
  • 工具->导入可信证书。

配置Web服务器

Web服务器必须请求证书,并根据信任库验证它们。 无法从Java请求证书。 每个Web服务器的配置都不同。 Github上的Look at AbstractContainerTest类中提供了Jetty配置。

将证书与帐户关联

每个证书由序列号和签署证书的证书颁发机构的名称唯一标识。 我们将它们与用户名和密码一起存储在数据库表中。 数据库更改位于db.changelog.xml文件中,有关新列,请参见changeset 3 ,有关数据初始化,请参见changeset 4 。

认证令牌

身份验证令牌表示身份验证尝试期间的用户数据和凭据。 它必须实现身份验证令牌接口,并保存我们希望在servlet过滤器和领域之间传递的所有数据。
由于我们希望同时使用用户名/密码和证书进行身份验证,因此我们扩展了UsernamePasswordToken类,并向其添加了证书属性。 新的身份验证令牌X509CertificateUsernamePasswordToken实现了新的接口X509CertificateAuthenticationToken ,两者在Github上都可用:

public class X509CertificateUsernamePasswordToken extends UsernamePasswordToken implements X509CertificateAuthenticationToken {private X509Certificate certificate;@Overridepublic X509Certificate getCertificate() {return certificate;}public void setCertificate(X509Certificate certificate) {this.certificate = certificate;}}

Servlet过滤器

Shiro过滤器将用户数据转换为身份验证令牌。 到目前为止,我们使用了FormAuthenticationFilter 。 如果传入的请求来自登录的用户,则过滤器将允许用户进入。如果用户尝试对自己进行身份验证,则过滤器将创建身份验证令牌并将其传递给框架。 否则,它将用户重定向到登录屏幕。
我们的过滤器CertificateOrFormAuthenticationFilter扩展了FormAuthenticationFilter 。

首先,我们必须说服它,不仅具有用户名和密码的请求,而且具有PGP证书的任何请求都可以被视为尝试登录。 其次,我们必须修改过滤器,以在身份验证令牌中发送PGP证书以及用户名和密码。
方法isLoginSubmission确定请求是否表示身份验证尝试:

@Overrideprotected boolean isLoginSubmission(ServletRequest request, ServletResponse response) {return super.isLoginSubmission(request, response) || isCertificateLogInAttempt(request, response);}private boolean isCertificateLogInAttempt(ServletRequest request, ServletResponse response) {return hasCertificate(request) && !getSubject(request, response).isAuthenticated();}private boolean hasCertificate(ServletRequest request) {return null != getCertificate(request);}private X509Certificate getCertificate(ServletRequest request) {X509Certificate[] attribute = (X509Certificate[]) request.getAttribute('javax.servlet.request.X509Certificate');return attribute==null? null : attribute[0];}

方法createToken创建身份验证令牌:

@Overrideprotected AuthenticationToken createToken(String username, String password, ServletRequest request, ServletResponse response) {boolean rememberMe = isRememberMe(request);String host = getHost(request);X509Certificate certificate = getCertificate(request);return createToken(username, password, rememberMe, host, certificate);}protected AuthenticationToken createToken(String username, String password, boolean rememberMe, String host, X509Certificate certificate) {return new X509CertificateUsernamePasswordToken(username, password, rememberMe, host, certificate);}

在配置文件中用CertificateOrFormAuthenticationFilter过滤器替换FormAuthenticationFilter:

[main]
# filter configuration
certificateFilter = org.meri.simpleshirosecuredapplication.servlet.CertificateOrFormAuthenticationFilter
# specify login page
certificateFilter.loginUrl = /simpleshirosecuredapplication/account/login.jsp
# name of request parameter with username; if not present filter assumes 'username'
certificateFilter.usernameParam = user
# name of request parameter with password; if not present filter assumes 'password'
certificateFilter.passwordParam = pass
# does the user wish to be remembered?; if not present filter assumes 'rememberMe'
certificateFilter.rememberMeParam = remember
# redirect after successful login
certificateFilter.successUrl  = /simpleshirosecuredapplication/account/personalaccountpage.jsp

将所有URL重定向到新的过滤器:

[urls]
# force ssl for login page 
/simpleshirosecuredapplication/account/login.jsp=ssl[8443], certificateFilter# only users with some roles are allowed to use role-specific pages 
/simpleshirosecuredapplication/repairmen/**=certificateFilter, roles[repairman]
/simpleshirosecuredapplication/sales/**=certificateFilter, roles[sales]
/simpleshirosecuredapplication/scientists/**=certificateFilter, roles[scientist]
/simpleshirosecuredapplication/adminarea/**=certificateFilter, roles[Administrator]# enable certificateFilter filter for all application pages
/simpleshirosecuredapplication/**=certificateFilter

自定义领域

我们的新领域将仅负责身份验证。 授权(访问权限)将由JNDIAndSaltAwareJdbcRealm处理。 只要PGP证书将用户身份验证为与用户名/密码相同的帐户,这种配置就起作用。 否则,新领域返回的主要主体必须与JNDIAndSaltAwareJdbcRealm返回的主要主体相同。
我们的领域不需要缓存,也不需要可选接口提供的任何其他服务。 因此,我们只需要实现两个接口:Realm和Nameable。
X509CertificateRealm仅支持带有PGP证书的身份验证令牌:

@Overridepublic boolean supports(AuthenticationToken token) {if (token!=null)return  token instanceof X509CertificateAuthenticationToken;return false;}

方法getAuthentcationInfo负责身份验证。 如果提供的证书有效并且与用户帐户关联,则领域创建身份验证信息对象。 请记住,主要主体必须与JNDIAndSaltAwareJdbcRealm返回的主体相同:

@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {// the cast is legal, since Shiro will let in only X509CertificateAuthenticationToken tokensX509CertificateAuthenticationToken certificateToken = (X509CertificateAuthenticationToken) token;X509Certificate certificate = certificateToken.getCertificate();// verify certificateif (!certificateOK(certificate)) {return null;}// the issuer name and serial number uniquely identifies certificateBigInteger serialNumber = certificate.getSerialNumber();String issuerName = certificate.getIssuerDN().getName();// find account associated with certificateString username = findUsernameToCertificate(issuerName, serialNumber);if (username == null) {// return null as no account was foundreturn null;}// sucesfull verification, return authentication inforeturn new SimpleAuthenticationInfo(username, certificate, getName());
}

请注意,该领域具有两个新属性:trustStore和trustStorePassword。 两者都是PGP证书验证所必需的。 与其他任何属性一样,两者都可以在配置文件中进行配置。
将新领域添加到Shiro.ini文件:

[main]
certificateRealm = org.meri.simpleshirosecuredapplication.realm.X509CertificateRealm
certificateRealm.trustStore=src/main/resources/truststore
certificateRealm.trustStorePassword=secret

现在可以使用PGP证书登录到应用程序。 如果证书不可用,则用户名和密码也可以使用。

应用程序源代码在Github上的'certificates_as_alternative_log_in_method'分支中可用。

多个领域

如果配置文件包含多个领域,则将全部使用。 在这种情况下,Shiro尝试使用所有已配置的领域对用户进行身份验证,并将身份验证结果合并在一起。 负责合并的对象称为身份验证策略。 框架提供了三种身份验证策略:

  • 所有成功的策略
  • 至少一项成功的策略 ,
  • 第一个成功的策略 。

默认情况下,使用“至少一个成功的策略”,这非常适合我们的目的。 同样,可以创建自定义身份验证策略。 例如,我们可能要求用户同时提供PGP证书和用户名/密码凭据才能登录。
新要求:用户必须同时提供PGP证书和用户名/密码凭据才能登录。

换句话说,我们需要的策略是:

  • 如果某些领域不支持令牌,则失败,
  • 如果某些领域无法验证用户身份,则失败,
  • 如果两个领域认证不同的主体,则失败。

认证策略是一个实现认证策略接口的对象。 在身份验证尝试之后和之前调用接口方法。 我们从“所有成功策略”(可用的最接近策略)创建“ 主要主体相同的身份验证策略 ”。 在每次领域身份验证尝试之后,我们将比较主体:

@Override
public AuthenticationInfo afterAttempt(...) {validatePrimaryPrincipals(info, aggregate, realm);return super.afterAttempt(realm, token, info, aggregate, t);
}private void validatePrimaryPrincipals(...) {...Object aggregPrincipal = aggregPrincipals.getPrimaryPrincipal();Object infoPrincipal = infoPrincipals.getPrimaryPrincipal();if (!aggregPrincipal.equals(infoPrincipal)) {String message = 'All realms are required to return the same primary principal. Offending realm: ' + realm.getName();log.debug(message);throw new AuthenticationException(message);}
}

身份验证策略在Shiro.ini文件中配置:

# multi-realms strategy
authenticationStrategy=org.meri.simpleshirosecuredapplication.authc.
PrimaryPrincipalSameAuthenticationStrategy
securityManager.authenticator.authenticationStrategy = $authenticationStrategy

最后,我们必须改回CertificateOrFormAuthenticationFilter的isLoginSubmission方法。 现在仅将具有用户名和密码的请求视为登录尝试。 证书不足:

@Override
protected boolean isLoginSubmission(ServletRequest request, ServletResponse response) {return super.isLoginSubmission(request, response);
}

如果立即运行该应用程序,则必须同时使用证书和用户名/密码登录方法。

该版本在Github上的'certificates_as_mandatory_log_in_method'分支中可用。

结束

此部分专用于Shiro领域。 我们创建了三个不同的应用程序版本,所有这些版本都可以在Github上获得。 它们涵盖了基本且可能是最重要的领域功能。

如果您需要了解更多信息,请从此处链接的类开始并阅读其javadocs。 他们写得很好,内容广泛。

参考: Apache Shiro第2部分–我们的JCG合作伙伴 Maria Jurcovicova在This is Stuff博客上获得的领域,数据库和PGP证书 。


翻译自: https://www.javacodegeeks.com/2012/05/apache-shiro-part-2-realms-database-and.html

apache shiro

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

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

相关文章

nginx 上传文件漏洞_文件上传及解析漏洞

注:本文仅供学习参考文件上传定义:文件上传漏洞是指网络攻击者上传了一个可执行的文件到服务器并执行。这里上传的文件可以是木马&#xff0c;病毒&#xff0c;恶意脚本或者WebShell等。这种攻击方式是最为直接和有效的&#xff0c;部分文件上传漏洞的利用技术门槛非常的低&…

Linux下暴力破解工具Hydra详解

Linux下暴力破解工具Hydra详解 一、简介 Number one of the biggest security holes are passwords, as every password security study shows. Hydra is a parallized login cracker which supports numerous protocols to attack. New modules are easy to add, beside that,…

具有FastUtil的精简Java集合

针对我最近在GNU Trove库上发表的《 发现Java原始资源集合的处理 》一书 &#xff0c; TheAlchemist指出了fastutil优于trove的一些优点&#xff1a;“我更喜欢fastutil&#xff08; http://fastutil.di.unimi.it/ &#xff09;&#xff0c;因为它仍在积极开发中&#xff0c;具…

android的json解析方式,Android解析JSON方式

JSON数据格式的定义&#xff1a;JSON的全称是&#xff1a;JavaScript Object Notation&#xff0c;是一种轻量级的数据交换格式。它构建于两种结构&#xff1a;1、"名称/值" 对的集合(a collection of name / value pairs)。不同的语言中&#xff0c;它被理解为对象(…

uniapp无法使用substr_关公战秦琼------Excel、SPSS Modler和R的使用对比(下)

5. 数据可视化Excel在Excel中对数据可视化除常用的图表外&#xff0c;还有样式、迷你图等直接和实际数据来共同展示效果。另外&#xff0c;还可以与切片器、PowerView等功能结合来动态可视化。Excel中图表的类型很多&#xff0c;如常见的散点图、折线图、柱形图、饼图等&#x…

第三次预作业20155231邵煜楠:虚拟机上的Linux学习

java第三次预作业——虚拟机初体验(学习记录) 学习在笔记本上安装Linux操作系统通过老师给予的官网链接&#xff0c;下载了VirtualBox-5.1.14-112924-win和Ubuntu-16.04-desktop-amd64.iso开始按照安装&#xff0c;尽管如此&#xff0c;还是遇到来了一些问题。 -在选择虚拟机的…

nginx_反向代理

nginx_反向代理 1 worker_processes 1;2 3 events {4 5 worker_connections 1024;6 7 }8 9 10 11 http { 12 13 include mime.types; 14 15 default_type application/octet-stream; 16 17 sendfile on; 18 19 keepalive_timeout …

miui11未升级android版本,MIUI 11稳定版正式推送,升级前你需要了解

原标题&#xff1a;MIUI 11稳定版正式推送&#xff0c;升级前你需要了解10月18日&#xff0c;MIUI官网正式宣布MIUI 11 正式推送&#xff0c;这是在小米MIX Alpha发布会之时同时推出的&#xff0c;在经过内测、公测后正式面向大众消费者推送。以下是一些您升级前需要了解的东西…

lumion自动保存_LUMION吊打MAX,轻松玩转规划渲染!

--文末获取&#xff1a;城市配景建筑模型包--大型场景渲染是LUMION渲染中难度最高的图纸首先&#xff0c;需要大量非常优质的配景包括&#xff1a;配景建筑、植物其次&#xff0c;对硬件要求也极高像这样的图纸渲染主体建筑周边需要布置大量的配景建筑没有个2080TI&#xff0c;…

在地图上 添加柱状_如何做出一份惊艳的PPT地图页?这2个字,你一定要知道!...

这篇文章&#xff0c;咱们就从一个读者私信给我的案例来说&#xff0c;就是这张PPT&#xff1a;他当时问我的问题是&#xff0c;怎么把这个表格优化的更美观&#xff1f;坦白讲&#xff0c;这是一个无解的问题&#xff0c;为什么这么说呢&#xff1f;咱们简单对这个图表分析&am…

akka与neety_Akka STM –与STM Ref和Agent一起打乒乓球

akka与neety乒乓是一个经典示例&#xff0c;其中2个玩家&#xff08;或线程&#xff09;访问共享资源–乒乓表并在彼此之间传递Ball&#xff08;状态变量&#xff09;。 使用任何共享资源&#xff0c;除非我们同步访问&#xff0c;否则线程可能会遇到潜在的死锁情况。 PingPong…

怎样调用另一个html中的元素,html – 使用DIV作为另一个元素的背景

这里我用2个div做了一个例子&#xff1a;> .content,包含前端所需的一切> .background – 包含文本,图像和背景中的所有其他内容要将一个div包装在另一个div上(制作叠加层),你必须将它们放入相同的元素中,在本例中它是#wrapper div.放置位置&#xff1a;包装的相对宽度/高…

3.1HTML5样式的使用

1.标签&#xff1a;<style>&#xff1a;样式定义<link>&#xff1a;资源引用 2.属性rel"stylesheet"&#xff1a;外部样式表type"text/css"&#xff1a;引入文档的类型margin-left&#xff1a;边距 3.三种样式表插入方法 外部样式表&#xff1…

cobbler的dhcp服务_Cobbler服务器安装部署

网络安装大家一定不陌生&#xff0c;一般通过Pxedhcpnfstftp类似配置即可实现&#xff0c;但安装过程中还需手动干预&#xff0c;后来加入了kickstart可全自动同时安装数百台系统&#xff0c;方便运维管理&#xff01;但kickstart存在可配置性差&#xff0c;会打断本网段其它电…

JIT编译器,内联和转义分析

即时&#xff08;JIT&#xff09; 即时&#xff08;JIT&#xff09;编译器是Java虚拟机的大脑。 JVM中对JIT编译器的影响最大。 一会儿&#xff0c;让我们退后一步&#xff0c;看看已编译和未编译语言的示例。 诸如Go&#xff0c;C和C 之类的语言之所以称为编译语言&#xff…

多步注册页面html模板,PHP HTML多步骤页面_php_开发99编程知识库

我正在做一个项目&#xff0c;向数据库Mysql添加信息。 下面是我们使用会话创建 3表单并按步骤提供用户注册和用户&#xff0c;以及这里会话保存记录&#xff0c;如果我在步骤2 或者步骤 2中可以回滚和编辑某些内容。 这应该用会话完成。 如果我做了什么&#xff0c;我希望你帮…

云顶之弈机器人法爆_云顶之弈10.16b机器人阵容推荐 云顶之弈10.16b机器人娱乐阵容玩法攻略...

云顶之弈10.16b机器人阵容推荐&#xff0c;在云顶之弈10.16b版本中机器人没有什么改动&#xff0c;所以以机器人为核心的娱乐阵容依然可以玩&#xff0c;而且运气好的话吃鸡也不是不可以&#xff0c;下面小编为大家带来了云顶之弈10.16b机器人娱乐阵容玩法攻略&#xff0c;一起…

在running android lint期间发生了内部错误.,Eclipse出现quot;Running Android Lint has encountered a problemquot...

近期打开Eclipse的时候&#xff0c;总是发生这种一个错误&#xff1a;"Running Android Lint has encountered a problem"。截图例如以下&#xff1a;。可是Eclipse能够正常执行程序。不会造成其它影响。可是每次打开Eclipse时。总是看到这个警告&#xff0c;心情会非…

分页JQ+模糊查询

--页面 <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <meta http-equiv"X-UA-Compatible" content"IEedge,chrome1"> <meta name"renderer" content"webkit|ie-stand"> <t…

idea 导出war包_使用IDEA实现远程代码DEBUG调试教程详解

我们在使用 IDEA DEBUG调试代码的时候&#xff0c;常常见到控制台会输出一句这样的话&#xff1a;「Connected to the target VM, address: 127.0.0.1:62981, transport: socket」&#xff0c;所以即使代码不在本地运行&#xff0c;只要JVM打开调试模式&#xff0c;并且网络能够…