搜索词>>SSL 耗时0.0030
  • tomcat如何配置SSL或者说HTTPS

    tomcat如何配置SSL或者说HTTPS,tomcat,https,SSL<h2>一、SSL是什么?HTTPS是什么?为何需要使用SSL</h2>   安全套接字层(SSL)是一种安全传输协议,用于在Internet上使用加密方法进行通信。SSL协议的主要目的是保证没有人能够篡改浏览器和web应用程序部署的服务器之间的通信。安全通信的另一个目的是根据SSL信息对服务器及其所有者进行身份验证,这样用户就可以确定正在访问的服务器是它正在访问的服务器。在常见的SSL场景中,当用户第一次访问web服务器时,服务器将其SSL证书或公钥发送给客户机。SSL证书包含关于服务器的信息,它的所有者、公司和它的有效期。如果用户不相信证书的真实性,就可以拒绝证书,从而有效地终止连接。如果用户接受该证书,证书本身就存储在浏览器中,并用于启动与发布服务器的安全连接。<br /> <br />    HTTP协议上的SSL协议通信称为HTTPS(安全HTTP)。例如,使用SSL加密连接的web站点在浏览器的地址栏中显示https作为协议名称。称为证书颁发机构(CA)的组织可以对SSL证书的详细信息进行身份验证,因此,如果用户信任CA,他们可以确保安全的web站点得到了认证,并且其详细信息是正确的。有许多ca可以颁发经过认证的SSL证书。现代浏览器自动识别最大和最知名的ca,并允许连接到提供这些组织认证的SSL证书的站点。如果SSL证书不是由CA认证的,或者由CA认证,但是没有被用户的浏览器识别,那么用户将会出现一个警告屏幕,在那里他或她可以决定是否信任该证书。 <p>tomcat配置SSL步骤包含</p> <p>1)生成密钥存储库</p> <p>2)在server.xml中更新连接器</p> <p>3)更新应用程序的web。xml与安全的网址<br />  </p> <h2>二、生成密钥对</h2>   SSL证书是JKS文件。JKS的格式代表Java密钥库,这是一个特定于Java的密钥存储库格式。JKS密钥库可以使用keytool实用程序创建和操作,从版本1.4发布它作为Java SDK的一部分。我们将使用Keytool创建一个自签名的SSL证书,它位于javahome/bin/目录中。 <pre> <code>//GOTO JAVA HOME c:\ > cd %JAVA_HOME%/bin //TYPE GENKEY COMMAND C:\BAML\DFCCUI\installs\jdk1.6\bin>keytool -genkey -alias tomcat -keyalg RSA Enter keystore password: Re-enter new password: What is your first and last name? [Unknown]: lokesh What is the name of your organizational unit? [Unknown]: boa What is the name of your organization? [Unknown]: boa What is the name of your City or Locality? [Unknown]: delhi What is the name of your State or Province? [Unknown]: delhi What is the two-letter country code for this unit? [Unknown]: 91 Is CN=lokesh, OU=boa, O=boa, L=delhi, ST=delhi, C=91 correct? [no]: yes Enter key password for <tomcat> (RETURN if same as keystore password): Re-enter new password: C:\installs\jdk1.6\bin></code></pre> 将会创建一个<code>.keystore</code> 文件在你的用户主目录中. 在windows 7系统中, 存放位置 <code>C:\Users\lokesh</code>.<br /> <em>也可以通过参数-keystore d:/leadsec指定创建的文件存放路径</em> <pre> <code>keytool -genkey -alias tomcat -keyalg RSA -keystore d:/mySSLKeystore</code></pre> <br /> <strong>提示:</strong><em>您只能为一个IP地址提供一个SSL证书。如果您在同一个IP上驻留多个域,那么只有一个主机名可以有一个与它的域名匹配的有效的SSL证书。如果您尝试在同一IP上使用SSL,那么浏览器将显示一个警告,即域名与证书不匹配。这是一个已知的SSL限制,因为SSL协议握手必须在从HTTP请求中提取主机名之前发生。</em> <h2>三、更新server.xml配置文件中的Connector</h2> <br /> Tomcat 能够使用两种 SSL 实现: <ul> <li>JSSE 实现,它是Java 运行时(从 1.4 版本起)的一部分。</li> <li>APR 实现,默认使用 OpenSSL 引擎。</li> </ul> 3.1JSSE 实现,它是Java 运行时(从 1.4 版本起)的一部分。 <p>打开你的tomcat安装位置,进入conf目录。在这个目录中你可以找到server.xml配置文件。打开这个配置文件并且找到下面的声明:<br />  </p> <pre> <code class="language-xml"><!-- <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" /> --></code></pre> 修改这个信息,取消注释。并且配置刚才生成的密码和秘钥的路径。 <pre> <code class="language-xml"><Connector SSLEnabled="true" acceptCount="100" clientAuth="false" disableUploadTimeout="true" enableLookups="false" maxThreads="25" port="8443" keystoreFile="C:/Users/lokesh/.keystore" keystorePass="password" protocol="org.apache.coyote.http11.Http11NioProtocol" scheme="https" secure="true" sslProtocol="TLS" /> </code></pre> 完成之后看看应用的变化。<br /> 配置ssl后需要将之前的8080注释掉 <pre> <code class="language-xml"> <!-- <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> --></code></pre> <br /> <br /> 访问https的地址为:http://ip:8443/projectname<br /> <strong><span style="color:#cc0000">注意:</span>直接输入ip:8443/projectname浏览器会默认http协议,必须手动输入https://</strong><br /> <br /> 3.2APR 实现,默认使用 OpenSSL 引擎。<br /> 指定 APR 连接器(APR 库必须可用),则使用: <pre> <code class="language-xml"><!-- Define a HTTP/1.1 Connector on port 8443, APR implementation --> <Connector protocol="org.apache.coyote.http11.Http11AprProtocol" port="8443" .../></code></pre> 如果使用 APR,则会出现一个选项,从而可以配置另一种 OpenSSL 引擎。 <pre> <code class="language-xml"><Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="someengine" SSLRandomSeed="somedevice" /></code></pre> 默认值为: <pre> <code class="language-xml"><Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" SSLRandomSeed="builtin" /></code></pre> 所以要想使用 APR 实现,一定要确保 <code>SSLEngine</code> 属性值不能为 <code>off</code>。该属性值默认为 <code>on</code>,如果指定的是其他值,它也会成为一个有效的引擎名称。<br /> 最后一步是在 <code>$CATALINA_BASE/conf/server.xml</code> 中配置连接器,<code>$CATALINA_BASE</code> 表示的是 Tomcat 实例的基本目录。Tomcat 安装的默认 <code>server.xml</code> 文件中包含一个用于 SSL 连接器的 <code><Connector></code> 元素的范例。APR 连接器会使用很多不同的属性来设置 SSL,特别是密钥和证书。 APR 配置范例如下: <pre> <code class="language-xml"><!-- Define a SSL Coyote HTTP/1.1 Connector on port 8443 --> <Connector protocol="org.apache.coyote.http11.Http11AprProtocol" port="8443" maxThreads="200" scheme="https" secure="true" SSLEnabled="true" SSLCertificateFile="/usr/local/ssl/server.crt" SSLCertificateKeyFile="/usr/local/ssl/server.pem" SSLVerifyClient="optional" SSLProtocol="TLSv1+TLSv1.1+TLSv1.2"/></code></pre>   <h2>四、修改应用中的web.xml配置文件使用安全URL</h2> 修改你的应用中的web.xml配置文件,如下: <pre> <code class="language-xml"><security-constraint> <web-resource-collection> <web-resource-name>application-one</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <user-data-constraint> <transport-guarantee>NONE</transport-guarantee> </user-data-constraint> </security-constraint></code></pre> <p>url模式被设置为/*因此,来自您的应用程序的任何页面/资源都是安全的(只能通过https访问)。传输保证标签被设置为机密,以确保你的应用能在SSL上运行。</p> <p>现在尝试访问该应用程序使用 <code>https://localhost:8443/application-one/</code>.这将在浏览器中显示证书信息。<br /> <img alt="证书" class="img-thumbnail" src="/assist/images/blog/9fc81441a2824b80a825bf843945352a.png" /><br /> 只有在您接受证书之后,它才会显示页面。<br /> <img alt="页面" class="img-thumbnail" src="/assist/images/blog/a92a483bfa3b46118c89af60a7af3f84.png" /><br /> 如果你没有在网络上设置“安全限制”。xml然后你将能够访问您的应用程序直接使用http://localhost:8080/application-one/.<br /> <img alt="未使用安全" class="img-thumbnail" src="/assist/images/blog/92ab402b9e65482dba2e943cfc75b226.png" /><br /> 这就是关于在tomcat服务器中实现SSL支持的简单而又重要的概念<br />  </p> <h2>五.使用场景</h2>   一定要注意的是,通常只有当 Tomcat 是独立运行的 Web 服务器时,才有必要去配置 Tomcat 以便利用加密套接字。具体细节可参看 <a href="http://tomcat.apache.org/tomcat-8.0-doc/security-howto.html" rel="external nofollow" target="_blank">Security Considerations Document</a>。当 Tomcat 以 Servlet/JSP 容器的形式在其他 Web 服务器(比如 Apache 或 Microsoft IIS)背后运行时,通常需要配置的是主 Web 服务器,用主服务器来处理与用户的 SSL 连接。主服务器一般会利用所有的 SSL 相关功能,将任何只有 Tomcat 才能处理的请求进行解密后再传给 Tomcat。同样,Tomcat 会返回明文形式的响应,这些响应在被传输到用户浏览器之前会被主服务器进行加密处理。在这种情境下,Tomcat 知道主服务器与客户端的所有通信都是通过安全连接进行的(因为应用要求),但 Tomcat 自身无法参与到加密与解密的过程中。 <h2>六.证书支持类型</h2> <p>  Tomcat 目前只能操作 <code>JKS</code>、<code>PKCS11</code>、<code>PKCS12</code> 格式的密钥存储库。<code>JKS</code> 是 Java 标准的“Java 密钥存储库”格式,是通过 <code>keytool</code> 命令行工具创建的。该工具包含在 JDK 中。<code>PKCS12</code> 格式一种互联网标准,可以通过 OpenSSL 和 Microsoft 的 Key-Manager 来。</p> <p>密钥存储库中的每一项都通过一个别名字符串来标识。尽管许多密码存储库实现都在处理别名时不区分大小写,但区分大小写的实现也是允许的。比如,<code>PKCS11</code> 规范需要别名是区分大小写的。为了避免别名大小写敏感的问题,不建议使用只有大小写不同的别名。</p> <p>为了将现有的证书导入 <code>JKS</code> 密码存储库,请查阅关于 <code>keytool</code> 的相关文档(位于 JDK 文档包里)。注意,OpenSSL 经常会在密码前加上易于理解的注释,但 <code>keytool</code> 并不支持这一点。所以如果证书里的密码数据前面有注释的话,在利用 <code>keytool</code> 导入证书前,一定要清除它们。</p> <p>要想把一个已有的由你自己的 CA 所签名的证书导入使用 OpenSSL 的 <code>PKCS12</code> 密码存储库,应该执行如下命令:</p> <pre> <code>openssl pkcs12 -export -in mycert.crt -inkey mykey.key -out mycert.p12 -name tomcat -CAfile myCA.crt -caname root -chain </code></pre>
  • HttpClient 使用SSL访问HTTPS(提高篇)

    本文将展示如何使用“接受所有”SSL支持来配置Apache HttpClient 4。目标很简单 - 使用没有有效证书的HTTPS URL。<h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>1.概述</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">本文将展示如何<strong>使用“接受所有”SSL支持</strong>来<strong>配置Apache HttpClient 4</strong>。目标很简单 - 使用没有有效证书的HTTPS URL。</span></span></span><br /> <br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>2. <em>SSLPeerUnverifiedException</em></strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">在没有使用<em>HttpClient</em>配置SSL的情况下,以下测试(使用HTTPS URL)将失败:</span></span></span></p> <pre> <code class="language-java">public class HttpLiveTest { @Test(expected = SSLPeerUnverifiedException.class) public void whenHttpsUrlIsConsumed_thenException() throws ClientProtocolException, IOException { DefaultHttpClient httpClient = new DefaultHttpClient(); String urlOverHttps = "https://localhost:8080/spring-security-rest-basic-auth"; HttpGet getMethod = new HttpGet(urlOverHttps); HttpResponse response = httpClient.execute(getMethod); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); } }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">确切的失败是:</p> <pre> <code class="language-java">javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:397) at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:126) ...</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">该<a href="http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLPeerUnverifiedException.html" rel="external nofollow" style="box-sizing:border-box; background-color:#ffffff; color:#63b175; text-decoration:none; font-family:raleway; font-size:18px; font-style:normal; font-variant-ligatures:normal; font-variant-caps:normal; font-weight:normal; letter-spacing:normal; orphans:2; text-align:start; text-transform:none; white-space:normal; widows:2; word-spacing:0px; -webkit-text-stroke-width:0px" target="_blank" title="Java SE 7中的SSLPeerUnverifiedException javadoc"><em>javax.net.ssl.SSLPeerUnverifiedException</em>例外</a>,只要有效信任链无法为URL建立发生。<br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>3.配置SSL - 全部接受(HttpClient <4.3)</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">现在让我们将HTTP客户端配置为信任所有证书链,而不管它们的有效性如何:</span></span></span></p> <pre> <code class="language-java">@Test public void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenException() throws IOException, GeneralSecurityException { TrustStrategy acceptingTrustStrategy = (cert, authType) -> true; SSLSocketFactory sf = new SSLSocketFactory( acceptingTrustStrategy, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("https", 8443, sf)); ClientConnectionManager ccm = new PoolingClientConnectionManager(registry); DefaultHttpClient httpClient = new DefaultHttpClient(ccm); String urlOverHttps = "https://localhost:8443/spring-security-rest-basic-auth/api/bars/1"; HttpGet getMethod = new HttpGet(urlOverHttps); HttpResponse response = httpClient.execute(getMethod); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">随着新的<em>TrustStrategy</em>现在<strong>覆盖标准证书验证过程</strong>(应该咨询配置的信任管理器) - 测试现在通过,<strong>客户端可以使用HTTPS URL</strong>。<br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>4. 带SSL 的Spring <em>RestTemplate</em>(HttpClient <4.3)</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">现在我们已经看到了如何配置一个支持SSL 的原始<em>HttpClient</em>,让我们来看看更高级别的客户端<em>--Spring RestTemplate</em>。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">未配置SSL,以下测试将按预期失败:</span></span></span></p> <pre> <code class="language-java">@Test(expected = ResourceAccessException.class) public void whenHttpsUrlIsConsumed_thenException() { String urlOverHttps = "https://localhost:8443/spring-security-rest-basic-auth/api/bars/1"; ResponseEntity<String> response = new RestTemplate().exchange(urlOverHttps, HttpMethod.GET, null, String.class); assertThat(response.getStatusCode().value(), equalTo(200)); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">那么让我们来配置SSL:<br />  </p> <pre> <code class="language-java">import static org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER; import java.security.GeneralSecurityException; import java.security.cert.X509Certificate; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.DefaultHttpClient; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestTemplate; ... @Test public void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenException() throws GeneralSecurityException { HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); DefaultHttpClient httpClient = (DefaultHttpClient) requestFactory.getHttpClient(); TrustStrategy acceptingTrustStrategy = (cert, authType) -> true SSLSocketFactory sf = new SSLSocketFactory( acceptingTrustStrategy, ALLOW_ALL_HOSTNAME_VERIFIER); httpClient.getConnectionManager().getSchemeRegistry() .register(new Scheme("https", 8443, sf)); String urlOverHttps = "https://localhost:8443/spring-security-rest-basic-auth/api/bars/1"; ResponseEntity<String> response = new RestTemplate(requestFactory). exchange(urlOverHttps, HttpMethod.GET, null, String.class); assertThat(response.getStatusCode().value(), equalTo(200)); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">如您所见,这<strong>与我们为原始HttpClient配置SSL的方式非常相似</strong> - 我们使用SSL支持配置请求工厂,然后实例化通过此预配置工厂的模板。<br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>5.配置SSL - 全部接受(HttpClient 4.4)</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">在HttpClient 4.4版本中,现在不推荐使用<em>SSLSocketFactory</em>,我们可以简单地配置我们的<em>HttpClient</em>,如下所示:</span></span></span></p> <pre> <code class="language-java">@Test public void givenIgnoringCertificates_whenHttpsUrlIsConsumed_thenCorrect() throws Exception { SSLContext sslContext = new SSLContextBuilder() .loadTrustMaterial(null, (certificate, authType) -> true).build(); CloseableHttpClient client = HttpClients.custom() .setSSLContext(sslContext) .setSSLHostnameVerifier(new NoopHostnameVerifier()) .build(); HttpGet httpGet = new HttpGet(HOST_WITH_SSL); httpGet.setHeader("Accept", "application/xml"); HttpResponse response = client.execute(httpGet); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>6. 带SSL 的Spring <em>RestTemplate</em>(HttpClient 4.4)</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">我们可以用同样的方法来配置我们的<em>RestTemplate</em>:</span></span></span></p> <pre> <code class="language-java">@Test public void givenAcceptingAllCertificatesUsing4_4_whenUsingRestTemplate_thenCorrect() throws ClientProtocolException, IOException { CloseableHttpClient httpClient = HttpClients.custom() .setSSLHostnameVerifier(new NoopHostnameVerifier()) .build(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); requestFactory.setHttpClient(httpClient); ResponseEntity<String> response = new RestTemplate(requestFactory).exchange( urlOverHttps, HttpMethod.GET, null, String.class); assertThat(response.getStatusCode().value(), equalTo(200)); }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>7.总结</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">本教程讨论了如何为Apache HttpClient配置SSL,以便它能够使用任何HTTPS URL,而不考虑证书。还展示了Spring <em>RestTemplate</em>的相同配置。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">然而,要理解的重要一点是,<strong>这种策略完全忽略了证书检查</strong> - 这使得它不安全,只能在有意义的地方使用。</span></span></span></p>
  • acme.sh 获取let's encrypt免费ssl证书https

    acme.sh 获取let's encrypt免费ssl证书httpsacme.sh 获取let's encrypt免费ssl证书https
  • MySQL5.6数据库双机主从热备配置

    MySQL5.6数据库双机主从热备配置<h2><span style="font-family:宋体">一、准备</span></h2> <p><span style="font-family:宋体">首先安装两个</span>MySQL<span style="font-family:宋体">数据库</span></p> <p>Ip<span style="font-family:宋体">地址和端口分别是</span>:</p> <p>192.168.8.202 3306</p> <p>192.168.8.203 3306</p> <p>Root<span style="font-family:宋体">用户及密码</span></p> <p>root root</p> <p><img alt="逻辑图" class="img-thumbnail" src="/assist/images/blog/87f121d199f44b569be494ca6c9226ca.png" /></p> <p><strong><span style="font-family:宋体"><span style="color:red">保证两个数据库服务器中需要同步的库内容一致</span></span></strong></p> <h2><span style="font-family:宋体">二、配置从数据库</span>Master</h2> <h3>2.1<span style="font-family:宋体">停止</span>master<span style="font-family:宋体">数据库服务</span></h3> <p><span style="font-family:宋体">命令</span>:</p> <pre> <code>#service mysqld stop</code></pre> <p>2.2<span style="font-family:宋体">修改</span>/etc/my.cnf<span style="font-family:宋体">配置文件</span></p> <p><span style="font-family:宋体">在</span>[mysqld]<span style="font-family:宋体">节点添加以下内容</span>:</p> <pre> <code>#######主从配置master信息####### #[必须]服务器唯一ID,默认是1,一般取IP最后一段 server_id=202 #[必须]启用二进制日志 log_bin=mysql-bin #需要备份的数据库名  多个库写多行 #binlog-do-db=test #binlog-do-db=test1 #binlog-do-db=test2 #忽略备份数据库名 多个库写多行 binlog-ignore-db=mysql #若涉及及同步函数或者存储过程需要配置,否则主备会产生异常不能同步 log_bin_trust_function_creators=TRUE #######主从配置master信息############</code></pre> <p> </p> <h3>2.3<span style="font-family:宋体">启动</span>MySQL<span style="font-family:宋体">数据库服务</span></h3> <p><span style="font-family:宋体">命令</span>:</p> <pre> <code>#service mysqld start</code></pre> <p>2.4<span style="font-family:宋体">添加从机过来同步数据的用户</span></p> <p><span style="font-family:宋体">首先使用</span>root<span style="font-family:宋体">用户登录</span>master<span style="font-family:宋体">数据库</span>,<span style="font-family:宋体">也就是</span>202</p> <p><span style="font-family:宋体">命令</span>:</p> <pre> <code>#mysql –uroot –proot</code></pre> <p><span style="font-family:宋体">登录数据库后执行命令</span><br />  </p> <pre> <code>mysql> grant replication slave on *.* to 'slaveuser'@'%' identified by '123456'; Query OK, 0 rows affected (0.06 sec) mysql> FLUSH PRIVILEGES; Query OK, 0 rows affected (0.06 sec)</code></pre> <p><strong><span style="font-family:宋体">提示</span></strong><strong>:</strong><em><span style="font-family:宋体">一般不用</span></em><em>root</em><em><span style="font-family:宋体">帐号,“</span></em><em>%</em><em><span style="font-family:宋体">”表示所有客户端都可能连,只要帐号,密码正确,此处可用具体客户端</span></em><em>IP</em><em><span style="font-family:宋体">代替,如</span></em><em>192.168.8.203</em><em><span style="font-family:宋体">,加强安全。</span></em></p> <p> </p> <p><span style="font-family:宋体">查看刚才创建的用户授权结果</span>:</p> <p><span style="font-family:宋体">命令</span>:</p> <p><span style="font-family:宋体">切换至</span>mysql<span style="font-family:宋体">数据库</span></p> <pre> <code>mysql> use mysql mysql>select * from user where host='%' and user='slaveuser' \G;  *************************** 1. row ***************************                   Host: %                   User: slaveuser               Password: *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9            Select_priv: N            Insert_priv: N            Update_priv: N            Delete_priv: N            Create_priv: N              Drop_priv: N            Reload_priv: N          Shutdown_priv: N           Process_priv: N              File_priv: N             Grant_priv: N        References_priv: N             Index_priv: N             Alter_priv: N           Show_db_priv: N             Super_priv: N  Create_tmp_table_priv: N       Lock_tables_priv: N           Execute_priv: N        Repl_slave_priv: Y       Repl_client_priv: N       Create_view_priv: N         Show_view_priv: N    Create_routine_priv: N     Alter_routine_priv: N       Create_user_priv: N             Event_priv: N           Trigger_priv: N Create_tablespace_priv: N               ssl_type:             ssl_cipher:            x509_issuer:           x509_subject:          max_questions: 0            max_updates: 0        max_connections: 0   max_user_connections: 0                 plugin: mysql_native_password  authentication_string:       password_expired: N 1 row in set (0.00 sec) ERROR: No query specified mysql></code></pre> <p> </p> <p><img alt="2" class="img-thumbnail" src="/assist/images/blog/42cf2e46dd9449e2a2ef048dd60c3c24.png" /></p> <p> </p> <p><strong>Repl_slave_priv</strong><strong><span style="font-family:宋体">项为</span>Y</strong><strong><span style="font-family:宋体">,表示授权成功</span></strong></p> <p> </p> <h3>2.5<span style="font-family:宋体">查看</span>master<span style="font-family:宋体">数据库状态</span></h3> <p><strong><span style="font-family:宋体">注意</span>:</strong><strong><span style="font-family:宋体">查看之前做好数据库只读操作</span>,</strong><strong><span style="font-family:宋体">防止在配置过程中出现写入数据日志,导致查询的</span>master</strong><strong><span style="font-family:宋体">日志不可控</span></strong></p> <p> </p> <p><span style="font-family:宋体">查看</span>master<span style="font-family:宋体">数据库状态</span></p> <p><span style="font-family:宋体">命令</span>:</p> <pre> <code>mysql> show master status; +------------------+----------+--------------+------------------+-------------------+ | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------+ | mysql-bin.000002 |     6187 |              | mysql            |                   | +------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.01 sec) mysql></code></pre> <p><img alt="3" class="img-thumbnail" src="/assist/images/blog/487df8f373c743aa85d47462fd11cb76.png" /></p> <p> </p> <p><span style="font-family:宋体">日志文件名</span>:<span style="font-family:宋体">称</span> <strong>mysql-bin.000002</strong></p> <p><span style="font-family:宋体">日志文件位置</span>: 6187</p> <p> </p> <h2><span style="font-family:宋体">三、配置从数据库</span>slave</h2> <p><strong><span style="font-family:宋体"><span style="color:red">如果</span></span><span style="color:red">master</span></strong><strong><span style="font-family:宋体"><span style="color:red">中需要同步的数据库已经存在且有数据</span></span><span style="color:red">,</span></strong><strong><span style="font-family:宋体"><span style="color:red">则需要将</span></span><span style="color:red">master</span></strong><strong><span style="font-family:宋体"><span style="color:red">中的数据库导出到</span></span><span style="color:red">slave</span></strong><strong><span style="font-family:宋体"><span style="color:red">保持</span></span><span style="color:red">master</span></strong><strong><span style="font-family:宋体"><span style="color:red">和</span></span><span style="color:red">slave</span></strong><strong><span style="font-family:宋体"><span style="color:red">需要同步的数据库信息一致。</span></span></strong></p> <h3>3.1<span style="font-family:宋体">停止</span>slave<span style="font-family:宋体">数据库服务</span></h3> <p><span style="font-family:宋体">命令</span>:</p> <pre> <code>#service mysqld stop</code></pre> <p>3.2<span style="font-family:宋体">修改</span>/etc/my.cnf<span style="font-family:宋体">配置文件</span></p> <p><span style="font-family:宋体">在</span>[mysqld]<span style="font-family:宋体">的节点下添加以下配置</span>:</p> <p><img alt="4" class="img-thumbnail" src="/assist/images/blog/d45976833db9461ca6985770eb53cbc0.png" /></p> <p><strong><span style="font-family:宋体">注意</span>ID</strong><strong><span style="font-family:宋体">不能重复</span></strong></p> <p> </p> <h3>3.3<span style="font-family:宋体">启动</span>slave<span style="font-family:宋体">的</span> MySQL<span style="font-family:宋体">数据库服务</span></h3> <p><span style="font-family:宋体">命令</span>:</p> <pre> <code>#service mysqld start</code></pre> <p>3.4root<span style="font-family:宋体">用户登录从库设置信息</span></p> <p><span style="font-family:宋体">登录数据库</span>:</p> <pre> <code>#mysql –uroot -proot</code></pre> <p><span style="font-family:宋体">停止</span>slave</p> <pre> <code>mysql> stop slave;</code></pre> <p><span style="font-family:宋体">配置</span>master<span style="font-family:宋体">信息</span>:</p> <pre> <code>mysql>change master to   master_host='192.168.8.202',  master_user='slaveuser',  master_password='123456',  master_log_file='mysql-bin.000002',  master_log_pos=6187;</code></pre> <p> </p> <p><em><span style="font-family:宋体">配置说明</span>:</em></p> <p><em>master_host:master</em><em><span style="font-family:宋体">的主机地址</span></em></p> <p><em>master_user:master</em><em><span style="font-family:宋体">上创建的同步数据用户</span>,</em><em><span style="font-family:宋体">之前创建的</span>slaveuser</em></p> <p><em>master_password:master</em><em><span style="font-family:宋体">创建的同步数据用户密码</span></em></p> <p><em>master_log_file:master</em><em><span style="font-family:宋体">最后一步查看的日志文件名</span></em></p> <p><em>master_log_pos:master</em><em><span style="font-family:宋体">最后一步查看的日志当前位置</span>,</em><em><span style="font-family:宋体">同步将从这个点开始</span></em></p> <p> </p> <p><span style="font-family:宋体">启动</span>slave</p> <pre> <code>mysql>start slave</code></pre> <p> </p> <h3>3.5<span style="font-family:宋体">查看从库配置状态</span></h3> <p><span style="font-family:宋体">命令</span>:</p> <pre> <code>Mysql> mysql> show slave status \G; *************************** 1. row ***************************                Slave_IO_State: Waiting for master to send event                   Master_Host: 192.168.8.202                   Master_User: slaveuser                   Master_Port: 3306                 Connect_Retry: 60               Master_Log_File: mysql-bin.000002           Read_Master_Log_Pos: 23305                Relay_Log_File: mysqld-relay-bin.000002                 Relay_Log_Pos: 283         Relay_Master_Log_File: mysql-bin.000002              Slave_IO_Running: Yes             Slave_SQL_Running: Yes               Replicate_Do_DB:           Replicate_Ignore_DB:            Replicate_Do_Table:        Replicate_Ignore_Table:       Replicate_Wild_Do_Table:   Replicate_Wild_Ignore_Table:                    Last_Errno: 0                    Last_Error:                  Skip_Counter: 0           Exec_Master_Log_Pos: 23305               Relay_Log_Space: 457               Until_Condition: None                Until_Log_File:                 Until_Log_Pos: 0            Master_SSL_Allowed: No            Master_SSL_CA_File:            Master_SSL_CA_Path:               Master_SSL_Cert:             Master_SSL_Cipher:                Master_SSL_Key:         Seconds_Behind_Master: 0 Master_SSL_Verify_Server_Cert: No                 Last_IO_Errno: 0                 Last_IO_Error:                Last_SQL_Errno: 0                Last_SQL_Error:   Replicate_Ignore_Server_Ids:              Master_Server_Id: 202                   Master_UUID: 4aed5689-6c69-11e7-9b1f-000c290e4f3d              Master_Info_File: /var/lib/mysql/master.info                     SQL_Delay: 0           SQL_Remaining_Delay: NULL       Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it            Master_Retry_Count: 86400                   Master_Bind:       Last_IO_Error_Timestamp:      Last_SQL_Error_Timestamp:                Master_SSL_Crl:            Master_SSL_Crlpath:            Retrieved_Gtid_Set:             Executed_Gtid_Set:                 Auto_Position: 0</code></pre> <p>1 row in set (0.00 sec)<img alt="5" class="img-thumbnail" src="/assist/images/blog/9e82bbab3212434b97e01481c2239796.png" /></p> <p><span style="font-family:宋体">注意</span>,<span style="font-family:宋体">上面两个配置</span></p> <p><span style="color:red">Slave_IO_Running: Yes</span></p> <p><span style="color:red">Slave_SQL_Running: Yes</span></p> <p><span style="color:red">Yes</span><span style="font-family:宋体"><span style="color:red">标识已经成功配置</span></span></p> <p> </p> <h2><span style="font-family:宋体">四、测试</span></h2> <h3>4.1<span style="font-family:宋体">表创建同步测试</span></h3> <p><span style="font-family:宋体">测试步骤方法</span>:<span style="font-family:宋体">在</span>master<span style="font-family:宋体">的</span>test<span style="font-family:宋体">数据库中创建一个表</span>,<span style="font-family:宋体">名称为</span>t_book<span style="font-family:宋体">,然后去</span>slave<span style="font-family:宋体">中</span>test<span style="font-family:宋体">数据库查看表是否同步。</span></p> <p> </p> <p><span style="font-family:宋体">首先查看数据库中已经有的表</span>:</p> <p><span style="color:red">Master</span><span style="font-family:宋体"><span style="color:red">中</span></span><span style="color:red">:</span></p> <pre> <code>#mysql –uroot –proot Mysql>use test show tables; +----------------+ | Tables_in_test | +----------------+ | t_user         | +----------------+ 1 row in set (0.00 sec) mysql></code></pre> <p><img alt="6" class="img-thumbnail" src="/assist/images/blog/0bf994d9b7db4ed587e91dc085d0dbe2.png" /></p> <p><span style="font-family:宋体">同样查看</span>slave<span style="font-family:宋体">库中的</span>test<span style="font-family:宋体">库</span></p> <p><img alt="7" class="img-thumbnail" src="/assist/images/blog/034b5e8cf80f47298f9d4801b8cbdeb8.png" /></p> <p> </p> <p><span style="font-family:宋体">在</span>master<span style="font-family:宋体">的</span>test<span style="font-family:宋体">库中创建一个表</span>,<span style="font-family:宋体">名称为</span>t_book</p> <p><span style="font-family:宋体">首先进入</span>MySQL<span style="font-family:宋体">数据库</span></p> <p> </p> <p><span style="font-family:宋体">创建表</span>t_book</p> <pre> <code>mysql> create table t_book(id int,book_name varchar(50)); Query OK, 0 rows affected (0.09 sec) mysql> show tables; +----------------+ | Tables_in_test | +----------------+ | t_book         | | t_user         | +----------------+ 2 rows in set (0.01 sec) mysql></code></pre> <p> </p> <p><img alt="8" class="img-thumbnail" src="/assist/images/blog/eb32f00cfba042d590533733be10f41f.png" /></p> <p> </p> <p><span style="font-family:宋体">查看</span>slave<span style="font-family:宋体">库中的</span>test</p> <p>Master<span style="font-family:宋体">已经在</span>test<span style="font-family:宋体">库中创建了一个</span>t_book<span style="font-family:宋体">的表格,现在查看</span>slave<span style="font-family:宋体">的</span>test<span style="font-family:宋体">数据库中是否已经同步</span></p> <p> </p> <p><span style="font-family:宋体">登录</span>slave</p> <pre> <code>#root –uroot –proot mysql> show tables; +----------------+ | Tables_in_test | +----------------+ | t_book         | | t_user         | +----------------+ 2 rows in set (0.00 sec) mysql></code></pre> <p><img alt="9" class="img-thumbnail" src="/assist/images/blog/4055bd7d4c2c4b87a9db93d542141050.png" /></p> <p><span style="font-family:宋体">可以在</span>slave<span style="font-family:宋体">中看到</span>t_book<span style="font-family:宋体">已经同步过来</span>,</p> <p><img alt="10" class="img-thumbnail" src="/assist/images/blog/98b8f78e613c40f8a12a68007ef00d1e.png" /></p> <p><span style="font-family:宋体">查询数据为空</span></p> <h3>4.2<span style="font-family:宋体">表数据同步测试</span></h3> <p><span style="font-family:宋体">测试方式</span>:</p> <p><span style="font-family:宋体">向</span>Master<span style="font-family:宋体">中的</span>test<span style="font-family:宋体">数据库中的</span>t_book<span style="font-family:宋体">表插入一条测试数据</span>,<span style="font-family:宋体">然后去</span>slave<span style="font-family:宋体">的</span>test<span style="font-family:宋体">数据库查询</span>t_book<span style="font-family:宋体">表中是否存在</span></p> <p> </p> <p>Master<span style="font-family:宋体">操作</span>:</p> <p><img alt="master" class="img-thumbnail" src="/assist/images/blog/2620001766974224ba9cd69408c9cdc6.png" /></p> <p> </p> <p>Slave<span style="font-family:宋体">操作</span>:</p> <p><img alt="slave" class="img-thumbnail" src="/assist/images/blog/b14bda207a9f432aae84d581f7ad8907.png" /></p> <p> </p> <p>Slave<span style="font-family:宋体">中的数据已经同步。</span></p> <p> </p>
  • spring boot项目中使用logback日志

    spring boot项目中使用logback日志,USING LOGBACK WITH SPRING BOOT ,Logback让一个优秀的企业应用程序的日志框架——它是快速,简单但强大的配置选项,有一个小的内存占用。我在介绍性的文章,介绍logback.Logback简介:一个企业日志框架。在Logback一系列的文章中,我讨论了如何使用XML配置Logback和Groovy。可选用的Logback配置:使用XML和Logback配置:使用Groovy.<h2>  引言</h2> <p>    Logback让一个优秀的企业应用程序的日志框架——它是快速,简单但强大的配置选项,有一个小的内存占用。我在介绍性的文章,介绍logback.Logback简介:一个企业日志框架。在Logback一系列的文章中,我讨论了如何使用XML配置Logback和Groovy。可选用的Logback配置:使用XML和Logback配置:使用Groovy.</p> <p>    在这篇文章中,我将讨论如何使用Logback spring boot 。虽然有许多Java日志记录选项,默认的 spring boot 选择使用Logback记录器。像许多东西在春天的引导,Logback默认配置合理的默认值。开箱即用的,spring boot使得Logback易于使用。</p> <h2>详细介绍</h2> <h4> </h4> <h3>创建日志记录器</h3> <p>    在以前的文章,我写过一篇关于如何用spring boot创建一个web应用。这里将会为这个web应用配置logback日志。这个应用包含  IndexController我们将添加日志记录代码。IndexController的代码是这样的。</p> <pre> <code class="language-java">package guru.springframework.controllers;     import guru.springframework.helpers.SpringLoggingHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;   @Controller public class IndexController {     private final Logger logger = LoggerFactory.getLogger(this.getClass());     @RequestMapping("/")     String index(){         logger.debug("This is a debug message");         logger.info("This is an info message");         logger.warn("This is a warn message");         logger.error("This is an error message");         new SpringLoggingHelper().helpMethod();         return "index";     } }</code></pre> <p>让我们添加一个 SpringLoggingHelper类与应用程序日志代码。虽然这类不做任何事除了释放日志语句,它将帮助我们理解配置日志记录在不同的包中。这是SpringLoggingHelper 的代码:</p> <pre> <code class="language-java">package guru.springframework.helpers;     import org.slf4j.Logger; import org.slf4j.LoggerFactory;   public class SpringLoggingHelper {     private final Logger logger = LoggerFactory.getLogger(this.getClass());     public void helpMethod(){         logger.debug("This is a debug message");         logger.info("This is an info message");         logger.warn("This is a warn message");         logger.error("This is an error message");       } }</code></pre> <p>在上面的类中,我们写日志代码对SLF4J API。SLF4J是常用的外墙日志框架,如Java Util日志,Log4J 2和Logback。通过编写SLF4J,我们的代码仍然和Logback解耦,从而提供插件不同的日志框架的灵活性,以后如果需要。</p> <p>如果你想知道关于SLF4J和Logback依赖性,你不需要指定任何。spring boot包含它们。假设您正在使用Maven或Gradle管理你春天启动项目,必要的依赖关系是 spring boot 下的依赖关系的一部分。</p> <p>运行 SpringBootWebApplication主类。当应用程序启动时,从你的浏览器的URL访问它,<strong>http://localhost:8080</strong></p> <p>IntelliJ控制台的日志输出。</p> <p><img alt="在弹簧启动时使用默认配置记录输出" class="img-thumbnail" src="https://i2.wp.com/springframework.guru/wp-content/uploads/2016/04/logging_output_with_springboot-1024x266.png?resize=863%2C224&ssl=1" /></p> <p>我们还没有写任何Logback配置。输出的 IndexController和 SpringLoggingHelper从logback根记录器类。注意,调试消息没有得到记录。Logback默认情况下将日志消息调试水平。然而, spring boot 团队给我们提供了一个默认的配置在theSpring Logback启动默认Logback配置文件, 基地.xml。此外,Spring提供了提供两个预配置的输出源通过引导 控制台- - - - - -appender.xml和 文件- - - - - -appender.xml文件。的 基地.xml文件referencesboth。</p> <p>这是base.xml的代码,文件从spring-boot github获取。</p> <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?>   <!-- Base logback configuration provided for compatibility with Spring Boot 1.1 -->   <included> <include resource="org/springframework/boot/logging/logback/defaults.xml" /> <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/> <include resource="org/springframework/boot/logging/logback/console-appender.xml" /> <include resource="org/springframework/boot/logging/logback/file-appender.xml" /> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="FILE" /> </root> </included></code></pre> <p>在这里您可以看到,通过将根记录器设置为INFO,Spring Boot已经覆盖了Logback的默认日志记录级别 ,这是上面示例中没有看到调试消息的原因。我们将在下一节中看到,在Spring Boot中更改日志级别非常简单。</p> <br /> 查看我的免费简介春季课程 <h4>通过Spring Boot的application.properties文件进行配置</h4> <p>在Spring Boot应用程序中,您可以外部化配置以在不同的环境中使用相同的应用程序代码。application.properties。属性文件可能是最受欢迎的几种不同的方法来外部化Spring Boot配置属性。在Spring Boot Web应用程序的默认结构中,可以找到application.properties。资源文件夹下的 属性文件。在application.properties中。属性文件,您可以定义Spring Boot的日志级别,应用程序记录器,Hibernate,Thymeleaf等。您还可以定义一个日志文件以将日志消息写入到控制台之外。</p> <p>下面是一个示例 应用。属性文件与日志配置。</p> <pre> <code>logging.level.org.springframework.web=INFO logging.level.guru.springframework.controllers=DEBUG logging.level.org.hibernate=ERROR logging.file=logs/spring-boot-logging.log</code></pre> <p><br /> 当您现在运行主类并访问应用程序时,将来自IndexController和 SpringLoggingHelper的日志消息 记录到控制台和logs / spring - boot - logging 。日志文件。<strong>注意</strong>:还有一个 日志记录。path属性来指定日志记录文件的路径。如果你使用它,Spring Boot会创建一个spring。日志文件在指定的路径。但是,您不能同时指定 日志记录。文件和 日志记录。路径属性在一起。如果完成,Spring Boot将忽略两者。</p> <p><img alt="使用application.properties启动Spring启动日志输出" class="img-thumbnail" src="https://i2.wp.com/springframework.guru/wp-content/uploads/2016/04/logging_output_with_application_properties-1024x486.png?resize=863%2C410&ssl=1" /></p> <p>在输出中,请注意IndexController的调试和更高级别的消息 已记录到控制台和文件中。这是因为在application.properties中。属性文件中,我们指定 DEBUG作为日志级 大师。springframework的。控制器包, IndexController是其中的一部分。由于我们没有明确配置 SpringLoggingHelper类,所以默认配置为 base 。使用了xml文件。因此,只有 记录了SpringLoggingHelper的INFO和更高级别的消息 。</p> <p>当您需要获取特定类或包的更详细的日志消息时,可以看到这是多么简单。</p> <h4>通过外部文件进行回溯配置</h4> <p>通过application.properties回溯配置。属性文件将足够用于许多Spring Boot应用程序。然而,大型企业应用程序可能会有更复杂的日志记录要求。如前所述,Logback通过<a href="https://springframework.guru/logback-configuration-using-xml/" rel="external nofollow" target="_blank">XML</a>和Groovy配置文件支持高级日志记录配置。</p> <p>在Spring Boot应用程序中,您可以将Logback XML配置文件指定为 logback 。xml或 logback - spring 。xml在项目类路径中。然而,Spring Boot团队建议使用<strong>-spring</strong>变体进行日志记录配置,   logback - spring 。XML优于  的logback 。xml。如果使用标准 的logback 。xml配置,Spring Boot可能无法完全控制日志的初始化。</p> <p>这是logback的代码 - spring 。xml文件。</p> <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <configuration>     <include resource="org/springframework/boot/logging/logback/base.xml"/>     <logger name="guru.springframework.controllers" level="WARN" additivity="false">         <appender-ref ref="CONSOLE"/>         <appender-ref ref="FILE"/>     </logger>     <logger name="guru.springframework.helpers" level="WARN" additivity="false">         <appender-ref ref="CONSOLE"/>         <appender-ref ref="FILE"/>     </logger> </configuration></code></pre> <p><br /> 通过更新的Spring Boot Logback配置,我们的日志输出现在如下所示:在上面的配置代码中,我们包括了 基础。第3行中的xml文件,请注意,我们没有配置任何appender。相反,我们依靠由Spring Boot提供的 CONSOLE和 FILE appender。</p> <p><img alt="记录Spring引导XML配置的输出" class="img-thumbnail" src="https://i0.wp.com/springframework.guru/wp-content/uploads/2016/04/logging_output_xml_configuration-1024x473.png?resize=863%2C399&ssl=1" /></p> <p><strong>注</strong>:Spring Boot要求 的logback - Spring。xml配置文件要在类路径上。但是,您可以将其存储在不同的位置,并使用日志记录指向它 。config属性在application.properties中。属性。</p> <h4>spring boot配置文件在日志记录中</h4> <p>在本地机器上开发时,通常将日志级别设置为 DEBUG。这将为您提供详细的日志消息供您开发使用。在生产时,它的典型设置将日志级别设置为 WARN或以上。这是为了避免在生产过程中运行时使用过多的调试信息和日志记录来填充日志。虽然日志记录非常有效,但仍然有成本。</p> <p>Spring Boot通过使用< springProfile >元素扩展用于回溯配置的Spring配置文件来解决这些要求 。使用你这个元素 的logback - spring。xml文件,您可以根据活动的spring boot配置文件选择性地包括或排除日志记录配置部分。</p> <p><strong>注意</strong>:在回弹配置中支持 < springProfile >可从SpringBoot 1.3.0.M2里程碑开始。</p> <p>以下是使用活动的Spring boot配置文件配置Logback的XML示例。</p> <p><br />  </p> <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <configuration>     <include resource="org/springframework/boot/logging/logback/base.xml" />     <springProfile name="dev,staging">         <logger name="guru.springframework.controllers" level="DEBUG" additivity="false">             <appender-ref ref="CONSOLE" />         </logger>>     </springProfile>     <springProfile name="production">         <logger name="guru.springframework.controllers" level="WARN" additivity="false">             <appender-ref ref="FILE" />         </logger>     </springProfile> </configuration></code></pre> <p>要将配置文件传递给应用程序,请使用- Dspring运行 应用程序。档案。active = JVM参数。在上面的配置代码中,对于 dev和 staging配置文件,我们配置了上 师。springframework的。控制器记录器将DEBUG和更高级别的消息记录到 控制台。对于 生产配置文件,我们配置了相同的记录器来将WARN和更高级别的消息记录到 文件中。</p> <p>对于本地开发,在IntelliJ中,选择<strong>Run-> Edit Configurations</strong>,并在<strong>Run / Debug Configurations</strong>对话框中设置JVM参数,如下所示。</p> <p><img alt="设置用于登录IntelliJ的活动配置文件" class="img-thumbnail" src="https://i0.wp.com/springframework.guru/wp-content/uploads/2016/04/RunDebug_Configurations_Dialog.png?resize=755%2C261&ssl=1" /></p> <p>现在,当我们使用dev配置文件运行应用程序时 ,我们将看到以下日志输出。</p> <p><img alt="使用弹簧活动配置文件记录输出" class="img-thumbnail" src="https://i1.wp.com/springframework.guru/wp-content/uploads/2016/04/logging_output_springl_profile.png?resize=863%2C171&ssl=1" /></p> <p>在上面的输出中,观察IndexController的日志记录输出 。 根据dev配置文件的配置,DEBUG和更高的日志消息记录到控制台 。您可以使用生产配置文件重新启动应用程序, 以确保将 WARN和较高日志消息记录到该文件中。</p> <h4>配置文件的条件处理</h4> <p>Logback支持在Janino库的帮助下对配置文件进行条件处理。您可以使用配置文件中的 < if >, < then >和 <else >元素来定位多个环境。要执行条件处理,请将Janino依赖项添加到您的Maven POM中,像这样。</p> <pre> <code class="language-xml"><dependency>    <groupId>org.codehaus.janino</groupId>    <artifactId>janino</artifactId>    <version>2.7.8</version> </dependency></code></pre>   <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <configuration>     <include resource="org/springframework/boot/logging/logback/base.xml" />     <springProfile name="dev,staging">         <logger name="guru.springframework.controllers" level="DEBUG" additivity="false">             <appender-ref ref="CONSOLE" />         </logger>>     </springProfile>     <springProfile name="production">     <logger name="guru.springframework.controllers" level="WARN" additivity="false">         <appender-ref ref="FILE" />     </logger>     </springProfile>     <if condition='property("spring.profiles.active").contains("dev")'>         <then>             <logger name="guru.springframework.helpers" level="DEBUG" additivity="false">                 <appender-ref ref="CONSOLE" />             </logger>         </then>         <else>             <logger name="guru.springframework.helpers" level="WARN" additivity="false">                 <appender-ref ref="FILE" />             </logger>         </else>     </if> </configuration></code></pre>   <p>完整的logback - spring。这是具有条件处理逻辑的xml文件。</p> <p> </p> <p>在上面的代码中,我们在< if >元素中指定了一个条件, 以检查当前活动的配置文件是否包含 dev。如果条件求 值为true,则< then >元素中的配置代码将 执行。在 < then >元素中,我们配置了 大师。springframework的。帮助人员将 DEBUG和更高级的消息记录到控制台。我们使用 < else >元素来配置记录器来将WARN和更高的消息记录到 日志文件中。所述 < 别的>元素执行用于比其它任何配置文件 dev的。</p> <p>当您使用生产配置文件运行应用程序 并进行访问时,两个记录器都会将 WARN和更高的消息记录到日志文件中,与此类似。<br /> <img alt="使用生产配置文件记录输出" class="img-thumbnail" src="https://i0.wp.com/springframework.guru/wp-content/uploads/2016/04/logging_output_production_profile.png?resize=642%2C319&ssl=1" /></p> <p>对于 dev配置文件,两个记录器都会将 DEBUG和更高级的消息记录到控制台,类似于此。<br /> <img alt="记录dev配置文件的输出" class="img-thumbnail" src="https://i2.wp.com/springframework.guru/wp-content/uploads/2016/04/logging_output_dev_profile.png?resize=863%2C222&ssl=1" /></p> <h4>logback自动扫描问题与spring boot</h4> <p>在的logback - spring。xml文件,您可以通过设置scan = “true”属性来启用配置的自动扫描 。启用自动扫描后,Logback扫描配置文件中的更改。对于任何更改,Logback自动重新配置它们。您可以通过将时间段传递给scanPeriod属性来指定扫描周期 ,其值以毫秒,秒,分钟或小时为单位指定。<br /> 例如,此代码告诉Logback扫描 logback - spring 。xml每10秒钟。</p> <pre> <code class="language-xml"><configuration debug="true" scan="true" scanPeriod="10 seconds" >   ... </configuration></code></pre>   <pre> <code>//Error on using auto-scan with springProfile   -ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:39 - no applicable action for [springProfile],   current ElementPath  is [[configuration][springProfile]]   //Error on using auto-scan with springProperty   -ERROR in ch.qos.logback.core.joran.spi.Interpreter@12:125 - no applicable action for [springProperty],     current ElementPath  is [[configuration][springProperty]]</code></pre>   <p>Spring Boot Logback的一个限制是使用 springProfile和 springProperty,设置自动扫描结果为错误。</p> <p>由于不兼容性问题而发生错误。Spring Boot使用 JoranConfigurator子类支持 springProfile和 springProperty。不幸的是,Logback的 ReconfigureOnChangeTask不提供挂接插件。</p> <h4>结论</h4> <p>Logback的流行趋势在于开源社区。许多受欢迎的开源项目使用Logback来记录日志需求。 <a href="https://camel.apache.org/" rel="external nofollow" target="_blank" title="阿帕奇骆驼">Apache Camel</a>,<a href="http://gradle.org/" rel="external nofollow" target="_blank" title="毕业">Gradle</a>和<a href="http://www.sonarqube.org/" rel="external nofollow" target="_blank">SonarQube</a>只是几个例子。</p> <p>回溯显然具有处理复杂企业应用程序登录需求的能力。所以,难怪Spring Boot团队为默认的日志记录实现选择了Logback。正如你在这篇文章中看到的,Spring Boot团队提供了与Logback的良好集成。开箱即用,Logback可以随Spring Boot一起使用。在这篇文章中,您已经看到,随着日志需求的发展,在spring boot中配置Logback是多么容易。<br /> <br /> 完整例子1:<br />  </p> <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。 scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。 debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 --> <configuration scan="false" scanPeriod="60 seconds" debug="false"> <property name="filePath" value="/aos/logs/axschoolweb.log" /> <property name="fileNamePattern" value="/aos/logs/axschoolweb" /> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <appender name="rollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${filePath}</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${fileNamePattern}.%d{yyyy-MM-dd}.log</fileNamePattern> </rollingPolicy> <encoder> <pattern>%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <!-- level等级顺序:error>warn>info>debug。 --> <logger name="org.springframework" level="WARN"/> <logger name="org.springframework.web" level="DEBUG"/> <logger name="org.springframework.remoting" level="WARN"/> <logger name="org.springframework.scheduling.quartz" level="WARN"/> <logger name="org.springframework.data.jpa" level="WARN"/> <logger name="org.cometd" level="WARN"/> <logger name="ch.qos.logback" level="WARN"/> <logger name="jdbc.sqltiming" level="WARN"/> <logger name="com.leftso" level="DEBUG"><!--根据具体项目配置--> <appender-ref ref="rollingFile"/> </logger> <!-- root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应, 要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。 --> <root level="DEBUG"> <appender-ref ref="console"/> <appender-ref ref="rollingFile"/> </root> </configuration></code></pre> <br /> <br /> 20171124更新=============================> <h3>logback配置打印mybaties sql日志</h3> <pre> <code class="language-xml"><logger name="net.xqlee.project.demo.dao" ><!--这里配置自己的dao(mybaties mapper接口)的包路径--> <level value="DEBUG"/> <appender-ref>console</appender-ref><!--指定一种或多种logback日志记录器--> </logger></code></pre> 如上代码片段所示,logback打开mybaties的sql打印日志需要创建一个logger并配置mybaties的dao所在包。然后将日志的等级调制DEBUG即可。日志的记录方式和其他logger一样可以配置多个。
  • HttpClient的RestTemplate - Java配置示例

    HttpClient的RestTemplate - Java配置示例<h2 style="margin-left:0px; margin-right:0px; text-align:start">HttpClient配置</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#333333"><span style="font-family:"varela round","helvetica neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">在<code>HttpClientConfig</code>课堂上,我们主要配置两件事 -</span></span></span></p> <ol style="margin-left:40px; margin-right:0px"> <li style="list-style-type:decimal"><code><a href="https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/conn/PoolingHttpClientConnectionManager.html" rel="external nofollow" style="box-sizing:border-box; transition:all 0.1s ease-in-out; color:#0366d6; text-decoration:none" target="_blank">PoolingHttpClientConnectionManager</a></code> - 顾名思义,它的连接池管理器。在这里,连接按照每个路线进行汇集。对于已经是管理器具有可用于池中的持续连接的路由的请求将是通过从池租用连接而不是创建全新连接的服务。 <p style="margin-left:0px; margin-right:0px"><code><a href="https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/conn/ConnectionKeepAliveStrategy.html" rel="external nofollow" style="box-sizing:border-box; transition:all 0.1s ease-in-out; color:#0366d6; text-decoration:none" target="_blank">ConnectionKeepAliveStrategy</a></code> 有助于设置时间,这决定了连接在重新使用之前可以保持空闲状态的时间。</p> </li> <li style="list-style-type:decimal">并设置一个<code>idleConnectionMonitor</code>线程,它会定期检查所有连接并释放尚未使用的空闲时间和空闲时间。</li> </ol> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#333333"><span style="font-family:"varela round","helvetica neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">真正使用的http客户端是<code><a href="https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/client/CloseableHttpClient.html" rel="external nofollow" style="box-sizing:border-box; transition:all 0.1s ease-in-out; color:#0366d6; text-decoration:none" target="_blank">CloseableHttpClient</a></code>bean。它将<code>RestTemplate</code>用于获取与API端点的连接。</span></span></span><br />  </p> <pre> <code class="language-java">import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.util.concurrent.TimeUnit; import org.apache.http.HeaderElement; import org.apache.http.HeaderElementIterator; import org.apache.http.HttpResponse; import org.apache.http.client.config.RequestConfig; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeaderElementIterator; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; import org.apache.http.ssl.SSLContextBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; /** * - Supports both HTTP and HTTPS * - Uses a connection pool to re-use connections and save overhead of creating connections. * - Has a custom connection keep-alive strategy (to apply a default keep-alive if one isn't specified) * - Starts an idle connection monitor to continuously clean up stale connections. */ @Configuration @EnableScheduling public class HttpClientConfig { private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientConfig.class); // Determines the timeout in milliseconds until a connection is established. private static final int CONNECT_TIMEOUT = 30000; // The timeout when requesting a connection from the connection manager. private static final int REQUEST_TIMEOUT = 30000; // The timeout for waiting for data private static final int SOCKET_TIMEOUT = 60000; private static final int MAX_TOTAL_CONNECTIONS = 50; private static final int DEFAULT_KEEP_ALIVE_TIME_MILLIS = 20 * 1000; private static final int CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS = 30; @Bean public PoolingHttpClientConnectionManager poolingConnectionManager() { SSLContextBuilder builder = new SSLContextBuilder(); try { builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); } catch (NoSuchAlgorithmException | KeyStoreException e) { LOGGER.error("Pooling Connection Manager Initialisation failure because of " + e.getMessage(), e); } SSLConnectionSocketFactory sslsf = null; try { sslsf = new SSLConnectionSocketFactory(builder.build()); } catch (KeyManagementException | NoSuchAlgorithmException e) { LOGGER.error("Pooling Connection Manager Initialisation failure because of " + e.getMessage(), e); } Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder .<ConnectionSocketFactory>create().register("https", sslsf) .register("http", new PlainConnectionSocketFactory()) .build(); PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); poolingConnectionManager.setMaxTotal(MAX_TOTAL_CONNECTIONS); return poolingConnectionManager; } @Bean public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() { return new ConnectionKeepAliveStrategy() { @Override public long getKeepAliveDuration(HttpResponse response, HttpContext context) { HeaderElementIterator it = new BasicHeaderElementIterator (response.headerIterator(HTTP.CONN_KEEP_ALIVE)); while (it.hasNext()) { HeaderElement he = it.nextElement(); String param = he.getName(); String value = he.getValue(); if (value != null && param.equalsIgnoreCase("timeout")) { return Long.parseLong(value) * 1000; } } return DEFAULT_KEEP_ALIVE_TIME_MILLIS; } }; } @Bean public CloseableHttpClient httpClient() { RequestConfig requestConfig = RequestConfig.custom() .setConnectionRequestTimeout(REQUEST_TIMEOUT) .setConnectTimeout(CONNECT_TIMEOUT) .setSocketTimeout(SOCKET_TIMEOUT).build(); return HttpClients.custom() .setDefaultRequestConfig(requestConfig) .setConnectionManager(poolingConnectionManager()) .setKeepAliveStrategy(connectionKeepAliveStrategy()) .build(); } @Bean public Runnable idleConnectionMonitor(final PoolingHttpClientConnectionManager connectionManager) { return new Runnable() { @Override @Scheduled(fixedDelay = 10000) public void run() { try { if (connectionManager != null) { LOGGER.trace("run IdleConnectionMonitor - Closing expired and idle connections..."); connectionManager.closeExpiredConnections(); connectionManager.closeIdleConnections(CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS, TimeUnit.SECONDS); } else { LOGGER.trace("run IdleConnectionMonitor - Http Client Connection manager is not initialised"); } } catch (Exception e) { LOGGER.error("run IdleConnectionMonitor - Exception occurred. msg={}, e={}", e.getMessage(), e); } } }; } }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start">RestTemplate配置</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#333333"><span style="font-family:"varela round","helvetica neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">在这里,我们正在配置<code>RestTemplate</code>我们最终将用来调用REST API的bean。如上所述,它使用<code>CloseableHttpClient</code>bean实例来构建<code><a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/client/ClientHttpRequestFactory.html" rel="external nofollow" style="box-sizing:border-box; transition:all 0.1s ease-in-out; color:#0366d6; text-decoration:none" target="_blank">ClientHttpRequestFactory</a></code>,用于创建<code>RestTemplate</code>。</span></span></span></p> <ol style="margin-left:40px; margin-right:0px"> <li style="list-style-type:decimal"><code><a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/client/HttpComponentsClientHttpRequestFactory.html" rel="external nofollow" style="box-sizing:border-box; transition:all 0.1s ease-in-out; color:#0366d6; text-decoration:none" target="_blank">HttpComponentsClientHttpRequestFactory</a></code>是<code>ClientHttpRequestFactory</code>使用<em>Apache HttpComponents HttpClient</em>创建请求的实现。</li> <li style="list-style-type:decimal">我们<code>@Scheduled</code>在<code>httpClient</code>配置中使用了注释。为了支持这个,我们必须添加对线程预定执行的支持。为此,我们使用了<code>ThreadPoolTaskScheduler</code>内部使用<a href="https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html" rel="external nofollow" style="box-sizing:border-box; transition:all 0.1s ease-in-out; color:#0366d6; text-decoration:none" target="_blank">ScheduledThreadPoolExecutor的</a> bean 来安排命令在给定的延迟后运行,或者定期执行。</li> </ol> <pre> <code class="language-java">import org.apache.http.impl.client.CloseableHttpClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.web.client.RestTemplate; public class RestTemplateConfig { @Autowired CloseableHttpClient httpClient; @Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory()); return restTemplate; } @Bean public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() { HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(); clientHttpRequestFactory.setHttpClient(httpClient); return clientHttpRequestFactory; } @Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setThreadNamePrefix("poolScheduler"); scheduler.setPoolSize(50); return scheduler; } }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start">如何使用RestTemplate</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#333333"><span style="font-family:"varela round","helvetica neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">要使用上述配置<code>RestTemplate</code>,只需将其注入控制器或测试类。</span></span></span></p> <pre> <code class="language-java">import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.web.client.RestTemplate; import com.leftso.config.HttpClientConfig; import com.leftso.config.RestTemplateConfig; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { RestTemplateConfig.class, HttpClientConfig.class }) public class TestApplication { @Autowired RestTemplate restTemplate; @Test public void getEmployees() { final String uri = "http://localhost:8080/employees"; String result = restTemplate.getForObject(uri, String.class); Assert.assertEquals(true, result.indexOf("Lokesh") > 0); } }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start">Maven的依赖</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#333333"><span style="font-family:"varela round","helvetica neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">首先,你将被要求有两个依赖关系,即<code>httpclient</code>和<code>spring-web</code>。我正在使用spring启动应用程序,所以pom文件如下所示:</span></span></span></p> <pre> <code class="language-xml"><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.leftso</groupId> <artifactId>springbootdemo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springbootdemo</name> <url>http://maven.apache.org</url> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> </parent> <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-hateoas</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project></code></pre>
  • Java编程之Apache Shiro Web支持

    组态,将Shiro集成到任何Web应用程序中的最简单的方法是在web.xml中配置Servlet ContextListener和Filter,了解如何读取Shiro的INI配置。<h1>组态</h1> <p>将Shiro集成到任何Web应用程序中的最简单的方法是在web.xml中配置Servlet ContextListener和Filter,了解如何读取Shiro的INI配置。大部分INI配置格式本身在配置页面的INI Sections部分中定义,但我们将在此处介绍一些其他特定于Web的部分。</p>  <strong>使用Spring?</strong> <hr /> <p>Spring Framework用户不会执行此设置。如果你使用Spring,你将需要阅读关于Spring特定的web配置。</p>   <h3>web.xml</h3> <h4>Shiro 1.2以后</h4> <p>在Shiro 1.2及更高版本中,标准Web应用程序通过添加以下XML块来初始化Shiro <code>web.xml</code>:</p> <pre> <code class="language-xml"><listener> <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class> </listener> ... <filter> <filter-name>ShiroFilter</filter-name> <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class> </filter> <filter-mapping> <filter-name>ShiroFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping> </code></pre> <p>这假设一个Shiro INI 配置文件位于以下两个位置之一,使用以前找到的位置:</p> <ol> <li><code>/WEB-INF/shiro.ini</code></li> <li><code>shiro.ini</code> 文件在类路径的根。</li> </ol> <p>这里是上面的配置:</p> <ul> <li> <p>的<code>EnvironmentLoaderListener</code>初始化四郎<code>WebEnvironment</code>实例(包含一切四郎需要操作,包括<code>SecurityManager</code>),并使其在访问<code>ServletContext</code>。如果您需要随时获取此<code>WebEnvironment</code>实例,则可以调用<code>WebUtils.getRequiredWebEnvironment(servletContext)</code>。</p> </li> <li> <p>该<code>ShiroFilter</code>会利用这个<code>WebEnvironment</code>来执行所有必要的安全操作的任何过滤的要求。</p> </li> <li> <p>最后,该<code>filter-mapping</code>定义确保所有请求都被过滤<code>ShiroFilter</code>,建议大多数Web应用程序使用,以确保任何请求都是安全的。</p> </li> </ul>  <strong>ShiroFilter过滤器映射</strong> <hr /> <p>通常希望在任何其他“filter-mapping”声明之前定义“ShiroFilter filter-mapping”,以确保Shiro也能在这些过滤器中运行。</p>   <h5>自定义<code>WebEnvironment</code>类</h5> <p>默认情况下,<code>EnvironmentLoaderListener</code>将创建一个<code>IniWebEnvironment</code>实例,它承担Shiro的基于INI的配置。如果愿意,您可以<code>WebEnvironment</code>通过在<code>ServletContext</code> <code>context-param</code>中指定一个自定义实例来指定<code>web.xml</code>:</p> <pre> <code class="language-xml"><context-param> <param-name>shiroEnvironmentClass</param-name> <param-value>com.foo.bar.shiro.MyWebEnvironment</param-value> </context-param> </code></pre> <p>这允许您自定义如何解析配置格式并将其表示为<code>WebEnvironment</code>实例。您可以对现有<code>IniWebEnvironment</code>的自定义行为进行子类化,或者完全支持不同的配置格式。例如,如果有人想要在XML而不是INI中配置Shiro,他们可以创建一个基于XML的实现,例如<code>com.foo.bar.shiro.XmlWebEnvironment</code>。</p> <h5>自定义配置位置</h5> <p>本<code>IniWebEnvironment</code>类希望读取并加载INI配置文件。默认情况下,此类将自动查找以下两个位置的Shiro <code>.ini</code>配置(按顺序):</p> <ol> <li><code>/WEB-INF/shiro.ini</code></li> <li><code>classpath:shiro.ini</code></li> </ol> <p>它将使用先找到的。</p> <p>然而,如果你希望将你的配置在其他位置,则可能与另一指定位置<code>context-param</code>在<code>web.xml</code>:</p> <pre> <code class="language-xml"><context-param> <param-name>shiroConfigLocations</param-name> <param-value>YOUR_RESOURCE_LOCATION_HERE</param-value> </context-param> </code></pre> <p>默认情况下,<code>param-value</code>期望由<code>ServletContext.getResource</code>方法定义的规则可解析。例如,<code>/WEB-INF/some/path/shiro.ini</code></p> <p>但是,您也可以使用Shiro的ResourceUtils类支持的适当资源前缀来指定特定的文件系统,类路径或URL位置,例如:</p> <ul> <li><code>file:/home/foobar/myapp/shiro.ini</code></li> <li><code>classpath:com/foo/bar/shiro.ini</code></li> <li><code>url:http://confighost.mycompany.com/myapp/shiro.ini</code></li> </ul> <h4>Shiro 1.1和更早版本</h4> <p>在1.1或更早版本的Web应用程序中启用Shiro的最简单的方法是定义IniShiroFilter并指定<code>filter-mapping</code>:</p> <pre> <code class="language-xml"><filter> <filter-name>ShiroFilter</filter-name> <filter-class>org.apache.shiro.web.servlet.IniShiroFilter</filter-class> </filter> ... <!-- Make sure any request you want accessible to Shiro is filtered. /* catches all --> <!-- requests. Usually this filter mapping is defined first (before all others) to --> <!-- ensure that Shiro works in subsequent filters in the filter chain: --> <filter-mapping> <filter-name>ShiroFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping> </code></pre> <p>此定义要求您的INI配置位于类路径(例如<code>classpath:shiro.ini</code>)的根目录下的shiro.ini文件中。</p> <h5>自定义路径</h5> <p>如果不想将INI配置放入<code>/WEB-INF/shiro.ini</code>或<code>classpath:shiro.ini</code>,您可以根据需要指定自定义资源位置。添加<code>configPath init-param</code>并指定资源位置:</p> <pre> <code class="language-xml"><filter> <filter-name>ShiroFilter</filter-name> <filter-class>org.apache.shiro.web.servlet.IniShiroFilter</filter-class> <init-param> <param-name>configPath</param-name> <param-value>/WEB-INF/anotherFile.ini</param-value> </init-param> </filter> ... </code></pre> <p>未限定(无限额或“非前缀”)<code>configPath</code>值被假定为<code>ServletContext</code>资源路径,可通过该<br /> <code>ServletContext.getResource</code>方法定义的规则解析。</p>  <strong>ServletContext资源路径 - Shiro 1.2+</strong> <hr /> <p>ServletContext资源路径在Shiro 1.2和更高版本中可用。在1.1和更早的版本中,所有<code>configPath</code>的定义必须指定<code>classpath:</code>,<code>file:</code>或<code>url:</code>前缀。</p> <p>你也可以指定其他非<code>ServletContext</code>使用资源位置<code>classpath:</code>,<code>url:</code>或<code>file:</code>前缀分别表示classpath中,URL或文件系统位置。例如:</p> <pre> <code class="language-xml">... <init-param> <param-name>configPath</param-name> <param-value>url:http://configHost/myApp/shiro.ini</param-value> </init-param> ... </code></pre> <h5>内联配置</h5> <p>最后,还可以将INI配置嵌入到web.xml中,而不使用INI文件。您可以使用<code>config init-param</code>而不是<code>configPath</code>:</p> <pre> <code class="language-xml"><filter> <filter-name>ShiroFilter</filter-name> <filter-class>org.apache.shiro.web.servlet.IniShiroFilter</filter-class> <init-param><param-name>config</param-name><param-value> # INI Config Here </param-value></init-param> </filter> ... </code></pre> <p>直列配置往往细小型或简单的应用,但它通常是更方便的外部化它的原因如下专用shiro.ini文件:</p> <ul> <li>您可以编辑安全配置很多,并且不想将修订控制'noise'添加到web.xml文件</li> <li>您可能希望将安全配置与web.xml配置的其余部分分离</li> <li>您的安全配置可能会变大,您希望保持web.xml精简并更易于阅读</li> <li>你有一个复杂的构建系统,其中相同的shiro配置可能需要在多个地方引用</li> </ul> <p>这取决于你 - 使用什么对你的项目有意义。</p> <h3>Web INI配置</h3> <p>除了标准<code>[main]</code>,<code>[users]</code>并<code>[roles]</code>在主已经说明部分配置章节中,你还可以指定一个特定的网络<code>[urls]</code>在部分<code>shiro.ini</code>文件:</p> <pre> <code># [main], [users] and [roles] above here ... [urls] ... </code></pre> <p>该<code>[urls]</code>部分允许你做一些事情,不以任何Web框架存在,我们见过的:确定特设的过滤器链在应用程序中任何匹配的URL路径的能力!</p> <p>这是<em>远远</em>比你通常定义过滤链更灵活,功能强大,简洁的<code>web.xml</code>:即使你从未使用过任何其他的功能,该功能提供四郎也只有这个使用,仅此一项就使值得使用。</p> <h4>[urls]</h4> <p>该<code>urls</code>节中每行的格式如下:</p> <pre> <code>_URL_Ant_Path_Expression_ = _Path_Specific_Filter_Chain_ </code></pre> <p>例如:</p> <pre> <code>... [urls] /index.html = anon /user/create = anon /user/** = authc /admin/** = authc, roles[administrator] /rest/** = authc, rest /remoting/rpc/** = authc, perms["remote:invoke"] </code></pre> <p>接下来,我们将详细介绍这些行的含义。</p> <p>等号(=)左侧的令牌是相对于Web应用程序的上下文根的Ant样式路径表达式。</p> <p>例如,假设您有以下<code>[urls]</code>行:</p> <pre> <code>/account/** = ssl, authc </code></pre> <p>该行指出:“以我的应用程序的路径的任何请求<code>/account</code>或任何它的子路径(<code>/account/foo</code>,<code>/account/bar/baz</code>,等),将触发”SSL,authc'过滤器链“。我们将在下面介绍过滤链。</p> <p>请注意,所有路径表达式都与应用程序的上下文根相关。这意味着如果你部署你的应用程序一天,<code>www.somehost.com/myapp</code>然后,然后将其部署到<code>www.anotherhost.com</code>(没有'myapp'子路径),模式匹配仍然会工作。所有路径都是相对于HttpServletRequest.getContextPath()值。</p>  <strong>订单事项!</strong> <hr /> <p>URL路径表达式根据传入请求按照它们定义的顺序和<em>第一个匹配的WINS</em>进行评估。例如,让我们假设有以下链定义:</p> <pre> <code>/account/** = ssl, authc /account/signup = anon </code></pre> <p>如果传入的请求打算到达<code>/account/signup/index.html</code>(所有“anon'ymous用户可访问),<em>它将永远不会被处理!</em>。原因是该<code>/account/**</code>模式首先匹配传入请求,并将所有剩余定义“短路”。</p> <p>始终记住根据<em>FIRST MATCH WINS</em>策略定义您的过滤器链!</p> <p> </p>   <h5>过滤器链定义</h5> <p>等号(=)右侧的令牌是以逗号分隔的过滤器列表,以对匹配该路径的请求执行。它必须匹配以下格式:</p> <pre> <code>filter1[optional_config1], filter2[optional_config2], ..., filterN[optional_configN] </code></pre> <p>哪里:</p> <ul> <li><em>filterN</em>是在<code>[main]</code>部分中定义的过滤器bean的名称</li> <li><code>[optional_configN]</code>是一个可选的括号字符串,对于<em>该特定路径</em>(每个过滤器,<em>特定</em>于<em>路径的</em>配置!)<em>的特定</em>过滤器具有含义。如果过滤器不需要为该URL路径指定特定的配置,您可以丢弃括号,<code>filterN[]</code>只是变成了<code>filterN</code>。</li> </ul> <p>并且因为过滤器令牌定义链(aka List),记住顺序很重要!按您希望请求流过链的顺序定义逗号分隔的列表。</p> <p>最后,如果不满足其必要条件(例如,执行重定向,使用HTTP错误代码进行响应,直接呈现等),则每个过滤器都可以自由地处理响应。否则,预期允许请求通过链继续到最终目的地视图。</p>  <strong>小费</strong> <hr /> <p>能够对路径特定配置(即<code>[optional_configN]</code>过滤器令牌的一部分)做出反应是对于Shiro过滤器可用的独特特征。</p> <p>如果你想创建自己的<code>javax.servlet.Filter</code>实现,也可以这样做,请确保你的过滤器子类org.apache.shiro.web.filter.PathMatchingFilter</p> <p> </p>   <h6>可用过滤器</h6> <p>可用于过滤器链定义的“池”过滤器在此<code>[main]</code>部分中定义。在主节中分配给它们的名称是在过滤器链定义中使用的名称。例如:</p> <pre> <code>[main] ... myFilter = com.company.web.some.FilterImplementation myFilter.property1 = value1 ... [urls] ... /some/path/** = myFilter </code></pre> <h2>默认过滤器</h2> <p>当运行Web应用程序时,Shiro将创建一些有用的默认<code>Filter</code>实例,并使其在<code>[main]</code>部分自动可用。您可以像配置<code>main</code>其他bean一样配置它们,并在链定义中引用它们。例如:</p> <pre> <code>[main] ... # Notice how we didn't define the class for the FormAuthenticationFilter ('authc') - it is instantiated and available already: authc.loginUrl = /login.jsp ... [urls] ... # make sure the end-user is authenticated. If not, redirect to the 'authc.loginUrl' above, # and after successful authentication, redirect them back to the original account page they # were trying to view: /account/** = authc ... </code></pre> <p>自动可用的默认Filter实例由DefaultFilter枚举定义,枚举的<code>name</code>字段是可用于配置的名称。他们是:</p> <table> <thead> <tr> <th>过滤器名称</th> <th>类</th> </tr> </thead> <tbody> <tr> <td>anon</td> <td>org.apache.shiro.web.filter.authc.AnonymousFilter</td> </tr> <tr> <td>authc</td> <td>org.apache.shiro.web.filter.authc.FormAuthenticationFilter</td> </tr> <tr> <td>authcBasic</td> <td>org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter</td> </tr> <tr> <td>登出</td> <td>org.apache.shiro.web.filter.authc.LogoutFilter</td> </tr> <tr> <td>noSessionCreation</td> <td>org.apache.shiro.web.filter.session.NoSessionCreationFilter</td> </tr> <tr> <td>烫发</td> <td>org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter</td> </tr> <tr> <td>港口</td> <td>org.apache.shiro.web.filter.authz.PortFilter</td> </tr> <tr> <td>休息</td> <td>org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter</td> </tr> <tr> <td>角色</td> <td>org.apache.shiro.web.filter.authz.RolesAuthorizationFilter</td> </tr> <tr> <td>ssl</td> <td>org.apache.shiro.web.filter.authz.SslFilter</td> </tr> <tr> <td>用户</td> <td>org.apache.shiro.web.filter.authc.UserFilter</td> </tr> </tbody> </table> <h2>启用和禁用过滤器</h2> <p>与任何过滤器链定义机制(<code>web.xml</code>,Shiro的INI等)的情况一样,只需通过将过滤器包含在过滤器链定义中来启用它,并通过从链定义中删除它来禁用它。</p> <p>但是在Shiro 1.2中添加的一个新功能是能够启用或禁用过滤器,而不从过滤器链中删除它们。如果启用(默认设置),则会按预期对请求进行过滤。如果禁用,则过滤器将允许请求立即传递到中的下一个元素<code>FilterChain</code>。您可以通常基于配置属性触发过滤器的启用状态,或者甚至可以<em>根据请求</em>触发它。</p> <p>这是一个强大的概念,因为与更改静态过滤器链定义(这将是永久和不灵活的)相比,基于某些要求启用或禁用过滤器通常更方便。</p> <p>Shiro通过其OncePerRequestFilter抽象父类完成此操作。所有Shiro的开箱即用的Filter实现子类化这一个,因此可以启用或禁用,而不从过滤器链中删除它们。你可以为你自己的过滤器实现子类这个类,如果你也需要这个功能*。</p> <p>* SHIRO-224希望能为任何过滤器启用此功能,而不只是那些子类<code>OncePerRequestFilter</code>。如果这对您很重要,请投票支持此问题。</p> <h3>一般启用/禁用</h3> <p>该OncePerRequestFilter(及其所有子类)支持启用/所有的请求,以及在每个请求的基础禁用。</p> <p>对所有请求的一般启用或禁用过滤器是通过将其<code>enabled</code>属性设置为true或false来实现的。默认设置是<code>true</code>因为大多数过滤器本身需要执行,如果它们在链中配置。</p> <p>例如,在shiro.ini中:</p> <pre> <code>[main] ... # configure Shiro's default 'ssl' filter to be disabled while testing: ssl.enabled = false [urls] ... /some/path = ssl, authc /another/path = ssl, roles[admin] ... </code></pre> <p>此示例显示可能许多URL路径都可能要求请求必须由SSL连接保护。在开发过程中设置SSL可能会令人沮丧和耗时。在开发过程中,可以禁用ssl过滤器。部署到生产时,您可以使用一个配置属性启用它 - 这比手动更改所有URL路径或维护两个Shiro配置要容易得多。</p> <h3>请求特定的启用/禁用</h3> <p><code>OncePerRequestFilter</code>实际上根据其<code>isEnabled(request, response)</code>方法确定是否启用或禁用过滤器。</p> <p>此方法默认返回属性的值,<code>enabled</code>用于一般启用/禁用所有请求,如上所述。如果要根据<em>请求特定</em>条件启用或禁用过滤器,则可以覆盖该<code>OncePerRequestFilter</code><code>isEnabled(request,response)</code>方法以执行更具体的检查。</p> <h3>路径特定的启用/禁用</h3> <p>Shiro的PathMatchingFilter(一个子类)<code>OncePerRequestFilter</code>能够基于被过滤的<em>特定路径</em>对配置做出反应,这意味着除了传入的请求和响应之外,您还可以基于路径和特定于路径的配置启用或禁用过滤器。</p> <p>如果您需要能够对匹配路径和路径特定配置做出反应,以确定是启用还是禁用过滤器,而不是<code>OncePerRequestFilter</code> <code>isEnabled(request,response)</code>覆盖方法,那么您将覆盖该<code>PathMatchingFilter</code> <code>isEnabled(request,response,path,pathConfig)</code>方法。</p> <h2>会话管理</h2> <h3>Servlet容器会话</h3> <p>在Web环境中,Shiro的默认会话管理器<code>SessionManager</code>实现是<code>ServletContainerSessionManager</code>。这个非常简单的实现将所有会话管理职责(包括如果servlet容器支持它的会话群集)委托给运行时Servlet容器。它本质上是一个桥梁,用于Shiro的会话API到servlet容器,没有别的。</p> <p>使用此默认值的一个好处是,使用现有servlet容器会话配置(超时,任何容器特定的集群机制等)的应用程序将按预期工作。</p> <p>这个默认的缺点是,你绑定到servlet容器的特定会话行为。例如,如果您想集群会话,但在生产中使用Jetty进行测试和Tomcat,则容器特定的配置(或代码)将不可移植。</p> <h4>Servlet容器会话超时</h4> <p>如果使用默认的servlet容器支持,您可以在Web应用程序的<code>web.xml</code>文件中按预期配置会话超时。例如:</p> <pre> <code class="language-xml"><session-config> <!-- web.xml expects the session timeout in minutes: --> <session-timeout>30</session-timeout> </session-config> </code></pre> <h3>本地会话</h3> <p>如果希望您的会话配置设置和集群在servlet容器(例如Jetty在测试中,但是Tomcat或JBoss在生产中)是可移植的,或者您想要控制特定的会话/集群功能,您可以启用Shiro的本地会话管理。</p> <p>“Native”这个词意味着Shiro自己的企业会话管理实现将用于支持所有<code>Subject</code>和<code>HttpServletRequest</code>会话,并完全绕过servlet容器。但是放心 - Shiro直接实现了Servlet规范的相关部分,所以任何现有的web / http相关代码如预期一样工作,并且不需要“知道”Shiro是透明地管理会话。</p> <h4>DefaultWebSessionManager</h4> <p>要为Web应用程序启用本机会话管理,您需要配置一个本地的Web会话管理器来覆盖默认的基于servlet容器的会话管理器。你可以通过配置<code>DefaultWebSessionManager</code>Shiro的实例来实现<code>SecurityManager</code>。例如,在<code>shiro.ini</code>:</p> <p><strong>shiro.ini本地Web会话管理</strong></p> <pre> <code>[main] ... sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager # configure properties (like session timeout) here if desired # Use the configured native session manager: securityManager.sessionManager = $sessionManager </code></pre> <p>一旦声明,您可以配置<code>DefaultWebSessionManager</code>实例与本地会话选项,如会话超时和聚类配置,如会话管理部分所述。</p> <h5>本地会话超时</h5> <p>配置<code>DefaultWebSessionManager</code>实例后,按会话管理:会话超时中所述配置会话超时</p> <h5>会话Cookie</h5> <p>在<code>DefaultWebSessionManager</code>支持两个特定网络的配置属性:</p> <ul> <li><code>sessionIdCookieEnabled</code> (布尔)</li> <li><code>sessionIdCookie</code>,一个Cookie实例。</li> </ul>  <strong>Cookie作为模板</strong> <hr /> <p>该<code>sessionIdCookie</code>属性本质上是一个模板 - 您配置<code>Cookie</code>实例属性,并且此模板将用于在运行时使用适当的会话ID值设置实际的HTTP Cookie。</p>   <h6>会话Cookie配置</h6> <p>DefaultWebSessionManager的<code>sessionIdCookie</code>默认实例是a <code>SimpleCookie</code>。这个简单的实现允许对要在http Cookie上配置的所有相关属性进行JavaBeans风格的属性配置。</p> <p>例如,您可以设置Cookie域:</p> <pre> <code>[main] ... securityManager.sessionManager.sessionIdCookie.domain = foo.com </code></pre> <p>有关其他属性,请参阅SimpleCookie JavaDoc。</p> <p>cookie的默认名称<code>JSESSIONID</code>与servlet规范一致。此外,Shiro的cookie支持<code>HttpOnly</code>标志。该<code>sessionIdCookie</code>套<code>HttpOnly</code>到<code>true</code>默认情况下,额外的安全性。</p>  <strong>注意</strong> <hr /> <p>Shiro的<code>Cookie</code>概念<code>HttpOnly</code>甚至在Servlet 2.4和2.5环境中支持该标志(而Servlet API只在2.6或更高版本中支持它)。</p>   <h6>禁用会话Cookie</h6> <p>如果您不想使用会话Cookie,可以通过将<code>sessionIdCookieEnabled</code>属性配置为false 来禁用它们。例如:</p> <p><strong>禁用本机会话Cookie</strong></p> <pre> <code>[main] ... securityManager.sessionManager.sessionIdCookieEnabled = false </code></pre> <h2>记住我的服务</h2> <p>Shiro将执行'rememberMe'服务,如果<code>AuthenticationToken</code>实现的<code>org.apache.shiro.authc.RememberMeAuthenticationToken</code>接口。此接口指定一个方法:</p> <pre> <code>boolean isRememberMe(); </code></pre> <p>如果此方法返回<code>true</code>,则Shiro将记住会话中的最终用户身份。</p>  <strong>UsernamePasswordToken和RememberMe</strong> <hr /> <p>常用的<code>UsernamePasswordToken</code>已经实现了<code>RememberMeAuthenticationToken</code>接口并支持rememberMe登录。</p>   <h3>计划支持</h3> <p>要以程序方式使用rememberMe,可以将值设置为<code>true</code>支持此配置的类。例如,使用标准<code>UsernamePasswordToken</code>:</p> <pre> <code>UsernamePasswordToken token = new UsernamePasswordToken(username, password); token.setRememberMe(true); SecurityUtils.getSubject().login(token); ... </code></pre> <h3>基于表单的登录</h3> <p>对于Web应用程序,<code>authc</code>过滤器默认为a <code>FormAuthenticationFilter</code>。这支持将“rememberMe”布尔作为form / request参数读取。默认情况下,它期望请求参数被命名<code>rememberMe</code>。这里是一个示例shiro.ini配置支持这:</p> <pre> <code>[main] authc.loginUrl = /login.jsp [urls] # your login form page here: login.jsp = authc </code></pre> <p>在您的网络表单中,有一个名为“rememberMe”的复选框:</p> <pre> <code><form ...> Username: <input type="text" name="username"/> <br/> Password: <input type="password" name="password"/> ... <input type="checkbox" name="rememberMe" value="true"/>Remember Me? ... </form> </code></pre> <p>默认情况下,<code>FormAuthenticationFilter</code>会寻找名为请求参数<code>username</code>,<code>password</code>和<code>rememberMe</code>。如果这些不同于您在表单中使用的表单字段名称,则需要在上配置名称<code>FormAuthenticationFilter</code>。例如,在<code>shiro.ini</code>:</p> <pre> <code>[main] ... authc.loginUrl = /whatever.jsp authc.usernameParam = somethingOtherThanUsername authc.passwordParam = somethingOtherThanPassword authc.rememberMeParam = somethingOtherThanRememberMe ... </code></pre> <h3>Cookie配置</h3> <p>您可以<code>rememberMe</code>通过设置默认的{{RememberMeManager}}的各种cookie属性来配置cookie的功能。例如,在shiro.ini中:</p> <pre> <code>[main] ... securityManager.rememberMeManager.cookie.name = foo securityManager.rememberMeManager.cookie.maxAge = blah ... </code></pre> <p>请参阅<code>CookieRememberMeManager</code>和支持<code>SimpleCookie</code>JavaDoc的配置属性。</p> <h3>自定义 <code>RememberMeManager</code></h3> <p>应该注意的是,如果默认的基于cookie的实现<code>RememberMeManager</code>不能满足你的需要,你可以插入任何你喜欢的<code>securityManager</code>类似,你将配置任何其他对象引用:</p> <pre> <code>[main] ... rememberMeManager = com.my.impl.RememberMeManager securityManager.rememberMeManager = $rememberMeManager </code></pre> <h2>JSP / GSP标签库</h2> <p>Apache Shiro提供了一个<code>Subject</code>-aware JSP / GSP标签库,允许您根据当前主题的状态控制您的JSP,JSTL或GSP页面输出。这对于基于查看网页的当前用户的身份和授权状态来个性化视图是非常有用的。</p> <h3>标签库配置</h3> <p>标签库描述符(TLD)文件捆绑在<code>shiro-web.jar</code>中<code>META-INF/shiro.tld</code>文件。要使用任何标记,请将以下行添加到JSP页面的顶部(或在您定义页面指令的任何位置):</p> <pre> <code><%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> </code></pre> <p>我们使用<code>shiro</code>前缀来指示shiro标签库命名空间,但是您可以分配任何您喜欢的名称。</p> <p>现在,我们将介绍每个标记,并说明如何使用它来呈现网页。</p> <h3>该<code>guest</code>标签</h3> <p><code>guest</code>只有当前<code>Subject</code>被视为“访客”时,标记才会显示其包装内容。客人是<code>Subject</code>没有身份的任何人。也就是说,我们不知道用户是谁,因为他们没有登录,他们不记得(从记住我服务)从以前的网站访问。</p> <p>例:</p> <pre> <code class="language-xml"><shiro:guest> Hi there! Please <a href="login.jsp">Login</a> or <a href="signup.jsp">Signup</a> today! </shiro:guest> </code></pre> <p>该<code>guest</code>标签是的逻辑相反的<code>user</code>标记。</p> <h3>该<code>user</code>标签</h3> <p><code>user</code>仅当当前<code>Subject</code>被视为“用户”时,标记才会显示其包装内容。在此上下文中的“用户”被定义为<code>Subject</code>具有已知标识,或者来自成功认证或者来自“记住我”服务。请注意,此标记在语义上与已认证的标记不同,后者比此标记更受限制。</p> <p>例:</p> <pre> <code class="language-xml"><shiro:user> Welcome back John! Not John? Click <a href="login.jsp">here<a> to login. </shiro:user> </code></pre> <p>该<code>user</code>标签是的逻辑相反的<code>guest</code>标记。</p> <h3>该<code>authenticated</code>标签</h3> <p>仅当当前用户<em>在其当前会话期间</em>已成功通过身份验证<em>时,</em>才显示主体内容。它比“用户”标签限制性更强。它在逻辑上与“notAuthenticated”标签相反。</p> <p>该<code>authenticated</code>标签将显示其包裹内容仅在当前<code>Subject</code>已成功验证<em>了当前会话中</em>。这是一个比用户更严格的标签,用于保证敏感工作流中的身份。</p> <p>例:</p> <pre> <code class="language-xml"><shiro:authenticated> <a href="updateAccount.jsp">Update your contact information</a>. </shiro:authenticated> </code></pre> <p>该<code>authenticated</code>标签是的逻辑相反的<code>notAuthenticated</code>标记。</p> <h3>该<code>notAuthenticated</code>标签</h3> <p>该<code>notAuthenticated</code>如果当前标签将显示其包裹内容<code>Subject</code>已<strong>不</strong>还成功地在本届会议期间进行身份验证。</p> <p>例:</p> <pre> <code class="language-xml"><shiro:notAuthenticated> Please <a href="login.jsp">login</a> in order to update your credit card information. </shiro:notAuthenticated> </code></pre> <p>该<code>notAuthenticated</code>标签是的逻辑相反的<code>authenticated</code>标记。</p> <h3>该<code>principal</code>标签</h3> <p>该<code>principal</code>标签将输出主题的<code>principal</code>(标识属性)或本金的属性。</p> <p>没有任何标记属性,标记将呈现<code>toString()</code>主体的值。例如(假设主体是字符串用户名):</p> <pre> <code class="language-xml">Hello, <shiro:principal/>, how are you today? </code></pre> <p>这是(大多数)等价于以下:</p> <pre> <code>Hello, <%= SecurityUtils.getSubject().getPrincipal().toString() %>, how are you today? </code></pre> <h4>类型主体</h4> <p>该<code>principal</code>标签假设默认情况下,该校长印的是<code>subject.getPrincipal()</code>价值。但是如果要打印一个<em>不是</em>主要主体的值,而是在主体的{ principal集合中另一个值,那么您可以通过类型获取该主体,并打印该值。</p> <p>例如,打印主题的用户ID(而不是用户名),假设ID在主体集合中:</p> <pre> <code>User ID: <principal type="java.lang.Integer"/> </code></pre> <p>这是(大多数)等价于以下:</p> <pre> <code>User ID: <%= SecurityUtils.getSubject().getPrincipals().oneByType(Integer.class).toString() %> </code></pre> <h4>主要财产</h4> <p>但是如果主体(上面的默认主要主体或者“类型化”主体)是一个复杂对象而不是一个简单的字符串,并且你想引用该主体上的一个属性呢?您可以使用该<code>property</code>属性指示要读取的属性的名称(必须通过JavaBeans兼容的getter方法访问)。例如(假设主要主体是User对象):</p> <pre> <code>Hello, <shiro:principal property="firstName"/>, how are you today? </code></pre> <p>这是(大多数)等价于以下:</p> <pre> <code>Hello, <%= SecurityUtils.getSubject().getPrincipal().getFirstName().toString() %>, how are you today? </code></pre> <p>或者,结合type属性:</p> <pre> <code>Hello, <shiro:principal type="com.foo.User" property="firstName"/>, how are you today? </code></pre> <p>这在很大程度上等同于以下:</p> <pre> <code>Hello, <%= SecurityUtils.getSubject().getPrincipals().oneByType(com.foo.User.class).getFirstName().toString() %>, how are you today? </code></pre> <h3>该<code>hasRole</code>标签</h3> <p><code>hasRole</code>仅当当前<code>Subject</code>分配了指定的角色时,标记才会显示其包装内容。</p> <p>例如:</p> <pre> <code><shiro:hasRole name="administrator"> <a href="admin.jsp">Administer the system</a> </shiro:hasRole> </code></pre> <p>该<code>hasRole</code>标签是的逻辑相反lacksRole标记。</p> <h3>该<code>lacksRole</code>标签</h3> <p><code>lacksRole</code>仅当当前<code>Subject</code> <strong>未</strong>分配指定角色时,标记才会显示其包装内容。</p> <p>例如:</p> <pre> <code class="language-xml"><shiro:lacksRole name="administrator"> Sorry, you are not allowed to administer the system. </shiro:lacksRole> </code></pre> <p>该<code>lacksRole</code>标签是的逻辑相反hasRole标记。</p> <h3>该<code>hasAnyRole</code>标签</h3> <p>该<code>hasAnyRole</code>如果当前标签将显示其包裹内容<code>Subject</code>被分配<em>任何</em>指定的角色从一个逗号分隔的角色名称列表。</p> <p>例如:</p> <pre> <code class="language-xml"><shiro:hasAnyRoles name="developer, project manager, administrator"> You are either a developer, project manager, or administrator. </shiro:lacksRole> </code></pre> <p>该<code>hasAnyRole</code>标签目前还没有一个逻辑相反的标记。</p> <h3>该<code>hasPermission</code>标签</h3> <p><code>hasPermission</code>只有当前的<code>Subject</code>“has”(暗示)指定的权限,标签才会显示其包装的内容。也就是说,用户具有指定的能力。</p> <p>例如:</p> <pre> <code class="language-xml"><shiro:hasPermission name="user:create"> <a href="createUser.jsp">Create a new User</a> </shiro:hasPermission> </code></pre> <p>该<code>hasPermission</code>标签是的逻辑相反lacksPermission标记。</p> <h3>该<code>lacksPermission</code>标签</h3> <p><code>lacksPermission</code>只有当前的<code>Subject</code> <strong>DOES没有</strong>(暗示)指定的权限,标签才会显示其包装的内容。也就是说,用户<strong>没有</strong>指定的能力。</p> <p>例如:</p> <pre> <code class="language-xml"><shiro:lacksPermission name="user:delete"> Sorry, you are not allowed to delete user accounts. </shiro:hasPermission> </code></pre> <p>该<code>lacksPermission</code>标签是的逻辑相反hasPermission标记。</p> <h2>借给文档</h2> <p>虽然我们希望本文档帮助您与Apache Shiro正在进行的工作,社区正在不断改进和扩展文档。如果您希望帮助Shiro项目,请考虑更正,扩展或添加您需要的文档。你提供的每一点帮助扩大了社区,反过来又改善了Shiro。</p> <p>提交文档的最简单方法是通过点击以下<code>Edit</code>链接提交拉取请求,将其发送到用户论坛或用户邮件列表。</p>
  • MySQL5.6配置双机互为主备

    MySQL5.6配置双机互为主备<h2><span style="font-family:宋体">一准备</span></h2> <p><span style="font-family:宋体">首先需要两个</span>MySQL<span style="font-family:宋体">数据库服务器</span></p> <p><span style="font-family:宋体">假如</span>:</p> <p><span style="font-family:宋体">数据库服务器</span>1<span style="font-family:宋体">信息</span></p> <p>192.168.8.202 3306 root root</p> <p><span style="font-family:宋体">数据库服务器</span>2<span style="font-family:宋体">信息</span></p> <p>192.168.8.203 3360 root root</p> <p> </p> <h2><span style="font-family:宋体">二、数据库</span>1<span style="font-family:宋体">配置</span></h2> <h3>2.1<span style="font-family:宋体">修改数据库</span>1<span style="font-family:宋体">配置</span></h3> <p><span style="font-family:宋体">编辑数据库</span>1<span style="font-family:宋体">的配置文件</span>/etc/my.cnf</p> <p><span style="font-family:宋体">在</span>[mysqld]<span style="font-family:宋体">节点下添加</span>:</p> <pre> <code>#######主从配置master信息####### #[必须]服务器唯一ID,默认是1,一般取IP最后一段 server_id=202 #[必须]启用二进制日志 log_bin=mysql-bin #需要备份的数据库名  多个库写多行 binlog-do-db=test #忽略备份数据库名 多个库写多行 #binlog-ignore-db=mysql #若涉及及同步函数或者存储过程需要配置,否则主备会产生异常不能同步 log_bin_trust_function_creators=TRUE #######主从配置master信息############ </code></pre> <p> </p> <p><img alt="1" class="img-thumbnail" src="/assist/images/blog/54fb33900cb949a2aea85ef489156cc6.png" /></p> <p> </p> <p><strong><span style="font-family:宋体"><span style="color:red">配置方式为指定数据库</span></span><span style="color:red">test</span></strong><strong><span style="font-family:宋体"><span style="color:red">同步</span></span></strong></p> <h3>2.2<span style="font-family:宋体">重启</span>MySQL<span style="font-family:宋体">服务</span></h3> <p><span style="font-family:宋体">重启数据库服务器</span>1<span style="font-family:宋体">的</span>MySQL<span style="font-family:宋体">服务</span></p> <p><span style="font-family:宋体">命令</span>:</p> <pre> <code>service mysqld restart</code></pre> <h3>2.3<span style="font-family:宋体">创建</span>slave<span style="font-family:宋体">用户</span></h3> <p><span style="font-family:宋体">创建一个</span>slave<span style="font-family:宋体">用户</span>,<span style="font-family:宋体">用于数据库服务器</span>2<span style="font-family:宋体">上进行同步</span></p> <p> </p> <p>Root<span style="font-family:宋体">登录</span>MySQL<span style="font-family:宋体">数据库</span></p> <pre> <code>[root@localhost ~]# mysql -uroot -proot</code></pre> <p> </p> <p><span style="font-family:宋体">创建</span>slave<span style="font-family:宋体">用户</span></p> <pre> <code>mysql> grant replication slave on *.* to 'slave'@'%' identified by '123456';</code></pre> <p> </p> <p><span style="font-family:宋体">切换至</span>mysql<span style="font-family:宋体">数据库</span>,<span style="font-family:宋体">查看刚才创建的用户信息</span></p> <pre> <code>mysql> use mysql Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> select * from user where User='slave' \G; *************************** 1. row ***************************                   Host: %                   User: slave               Password: *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9            Select_priv: N            Insert_priv: N            Update_priv: N            Delete_priv: N            Create_priv: N              Drop_priv: N            Reload_priv: N          Shutdown_priv: N           Process_priv: N              File_priv: N             Grant_priv: N        References_priv: N             Index_priv: N             Alter_priv: N           Show_db_priv: N             Super_priv: N  Create_tmp_table_priv: N       Lock_tables_priv: N           Execute_priv: N        Repl_slave_priv: Y       Repl_client_priv: N       Create_view_priv: N         Show_view_priv: N    Create_routine_priv: N     Alter_routine_priv: N       Create_user_priv: N             Event_priv: N           Trigger_priv: N Create_tablespace_priv: N               ssl_type:             ssl_cipher:            x509_issuer:           x509_subject:          max_questions: 0            max_updates: 0        max_connections: 0   max_user_connections: 0                 plugin: mysql_native_password  authentication_string:       password_expired: N 1 row in set (0.00 sec) ERROR: No query specified mysql></code></pre> <p><img alt="2" class="img-thumbnail" src="/assist/images/blog/d3716e0e60d7453290b3af342cf6878d.png" /></p> <p><span style="font-family:宋体">注意查看</span>: <strong><span style="color:red">Repl_slave_priv: Y</span></strong></p> <p><span style="font-family:宋体">是</span>Y<span style="font-family:宋体">标识该账户已经有权限操作同步了。</span></p> <h3>2.3<span style="font-family:宋体">备份数据库</span></h3> <p><span style="font-family:宋体">该操作主要是备份<strong>数据库服务器</strong></span><strong>1</strong><span style="font-family:宋体">中需要同步的数据库。(这里的同步数据库为</span>test<span style="font-family:宋体">库)</span></p> <p> </p> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">锁表为只读状态</span></li> </ol> <p style="margin-left:18.0pt"><span style="font-family:宋体">锁表命令</span>:<br />  </p> <pre> <code>mysql> FLUSH TABLES WITH READ LOCK; Query OK, 0 rows affected (0.06 sec) </code></pre> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">备份数据库</span>1<span style="font-family:宋体">数据</span></li> </ol> <p><span style="font-family:宋体">退出数据库或者新打开一个</span>ssh<span style="font-family:宋体">连接</span>,<span style="font-family:宋体">执行以下命令备份</span></p> <pre> <code>[root@localhost ~]# mysqldump -uroot -proot test > /root/test-db.sql</code></pre> <p><img alt="3" class="img-thumbnail" src="/assist/images/blog/22d428f5384146fda237607774502347.png" /></p> <p> </p> <p><span style="font-family:宋体">如图已经将</span>test<span style="font-family:宋体">库备份至</span>/root<span style="font-family:宋体">目录</span>,<span style="font-family:宋体">名称为</span>test-db.sql</p> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">查看数据库服务器</span>1<span style="font-family:宋体">的</span>master<span style="font-family:宋体">信息</span></li> </ol> <p style="margin-left:18.0pt"><span style="font-family:宋体">登录数据库执行命令</span>:</p> <pre> <code>mysql> show master status; +------------------+----------+--------------+------------------+-------------------+ | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------+ | mysql-bin.000003 |      887 | test         |                  |                   | +------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec) mysql></code></pre> <p><img alt="4" class="img-thumbnail" src="/assist/images/blog/74ce9951b1034a9088bc7daf619e1785.png" /></p> <p><strong><span style="font-family:宋体">记住这里的</span>file</strong><strong><span style="font-family:宋体">名称和</span>Position</strong><strong><span style="font-family:宋体">值</span></strong></p> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">解锁表</span></li> </ol> <pre> <code>mysql> unlock tables;</code></pre> <p> </p> <h2><span style="font-family:宋体">三、数据库</span>2<span style="font-family:宋体">配置</span></h2> <h3>3.1<span style="font-family:宋体">修改数据库</span>2<span style="font-family:宋体">配置</span></h3> <p><span style="font-family:宋体">编辑数据库</span>1<span style="font-family:宋体">的配置文件</span>/etc/my.cnf</p> <p><span style="font-family:宋体">在</span>[mysqld]<span style="font-family:宋体">节点下添加</span>:</p> <pre> <code>#######主从配置master信息####### #[必须]服务器唯一ID,默认是1,一般取IP最后一段 server_id=203 #[必须]启用二进制日志 log_bin=mysql-bin #需要备份的数据库名  多个库以逗号分隔 binlog-do-db=test #忽略备份数据库名 #binlog-ignore-db=mysql #若涉及及同步函数或者存储过程需要配置,否则主备会产生异常不能同步 log_bin_trust_function_creators=TRUE #######主从配置master信息############</code></pre> <h3>3.2<span style="font-family:宋体">重启</span>MySQL<span style="font-family:宋体">服务</span></h3> <p><span style="font-family:宋体">重启数据库服务器</span>2<span style="font-family:宋体">的</span>MySQL<span style="font-family:宋体">服务</span></p> <p><span style="font-family:宋体">命令</span>:</p> <pre> <code>#service mysqld restart</code></pre> <p> </p> <h3>3.3<span style="font-family:宋体">导入数据</span></h3> <p><span style="font-family:宋体">将数据库服务器</span>1<span style="font-family:宋体">中导出的库导入进来</span></p> <p> </p> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">远程复制备份文件</span></li> </ol> <pre> <code>[root@bogon ~]# scp root@192.168.8.202:/root/test-db.sql /root/test-db.sql The authenticity of host '192.168.8.202 (192.168.8.202)' can't be established. RSA key fingerprint is 70:bc:ba:69:58:77:3c:4b:8e:75:75:91:35:fa:40:f6. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '192.168.8.202' (RSA) to the list of known hosts. root@192.168.8.202's password: test-db.sql                                                                                                                                                                                                                                 100% 2575     2.5KB/s   00:00    [root@bogon ~]# ll total 42432 -rw-------. 1 root root     3330 Jul 19 03:54 anaconda-ks.cfg -rw-r--r--. 1 root root 41789864 Jul 25 07:10 axshool25.sql drwxr-xr-x. 2 root root     4096 Jul 19 07:11 Desktop drwxr-xr-x. 2 root root     4096 Jul 19 07:11 Documents drwxr-xr-x. 2 root root     4096 Jul 19 07:11 Downloads drwxr-xr-x. 4 root root     4096 Jul 24 06:08 fastDFS -rw-r--r--. 1 root root    41364 Jul 19 03:54 install.log -rw-r--r--. 1 root root     9154 Jul 19 03:52 install.log.syslog drwxr-xr-x. 2 root root     4096 Jul 19 07:11 Music drwxr-xr-x. 2 root root     4096 Jul 19 07:11 Pictures drwxr-xr-x. 2 root root     4096 Jul 19 07:11 Public drwxrwxr-x. 6 root root     4096 May 17 08:39 redis-3.2.9 -rw-r--r--. 1 root root  1547695 May 17 08:40 redis-3.2.9.tar.gz drwxr-xr-x. 2 root root     4096 Jul 19 07:11 Templates -rw-r--r--. 1 root root     2575 Jul 26 03:00 test-db.sql drwxr-xr-x. 2 root root     4096 Jul 19 07:11 Videos [root@bogon ~]# 导入数据 [root@bogon ~]# mysql -uroot -proot -B test </root/test-db.sql</code></pre> <p> </p> <h3>3.4<span style="font-family:宋体">创建</span>slave<span style="font-family:宋体">用户</span></h3> <p>Root<span style="font-family:宋体">用户登录数据库</span></p> <pre> <code>[root@bogon ~]# mysql -uroot -proot</code></pre> <p> </p> <p><span style="font-family:宋体">创建</span>slave<span style="font-family:宋体">用户</span></p> <pre> <code>mysql> grant replication slave on *.* to 'slave'@'%' identified by '123456';</code></pre> <p> </p> <p><span style="font-family:宋体">切换至</span>mysql<span style="font-family:宋体">数据库</span>,<span style="font-family:宋体">查看刚才创建的用户信息</span></p> <pre> <code>mysql> use mysql Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> select * from user where User='slave' \G; *************************** 1. row ***************************                   Host: %                   User: slave               Password: *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9            Select_priv: N            Insert_priv: N            Update_priv: N            Delete_priv: N            Create_priv: N              Drop_priv: N            Reload_priv: N          Shutdown_priv: N           Process_priv: N              File_priv: N             Grant_priv: N        References_priv: N             Index_priv: N             Alter_priv: N           Show_db_priv: N             Super_priv: N  Create_tmp_table_priv: N       Lock_tables_priv: N           Execute_priv: N        Repl_slave_priv: Y       Repl_client_priv: N       Create_view_priv: N         Show_view_priv: N    Create_routine_priv: N     Alter_routine_priv: N       Create_user_priv: N             Event_priv: N           Trigger_priv: N Create_tablespace_priv: N               ssl_type:             ssl_cipher:            x509_issuer:           x509_subject:          max_questions: 0            max_updates: 0        max_connections: 0   max_user_connections: 0                 plugin: mysql_native_password  authentication_string:       password_expired: N 1 row in set (0.00 sec) ERROR: No query specified mysql></code></pre> <p><img alt="5" class="img-thumbnail" src="/assist/images/blog/dc22d4ee5d40435599149de8f38a43cb.png" /></p> <p> </p> <h3>3.5<span style="font-family:宋体">配置</span>master</h3> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">首先停止</span>slave</li> </ol> <pre> <code>mysql>stop slave</code></pre> <p> </p> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">配置</span>master<span style="font-family:宋体">指向数据库服务器</span>1:</li> </ol> <pre> <code>mysql> change master to       -> master_host='192.168.8.202',      -> master_user='slave',      -> master_password='123456',      -> master_log_file='mysql-bin.000003',      -> master_log_pos=887; Query OK, 0 rows affected, 2 warnings (0.05 sec) mysql></code></pre> <p> </p> <p><em><span style="font-family:宋体">命令说明</span>:</em></p> <p><em>master_host              :master</em><em><span style="font-family:宋体">的主机地址</span></em></p> <p><em>master_user              :master</em><em><span style="font-family:宋体">的备份用户</span></em></p> <p><em>master_password    :master</em><em><span style="font-family:宋体">备份用户密码</span></em></p> <p><em>master_log_file        :</em><em><span style="font-family:宋体">日志文件名</span>,</em><em><span style="font-family:宋体">在第二步骤的</span>2.3</em><em><span style="font-family:宋体">备份的最后一步查看的信息里面</span></em></p> <p><em>master_log_pos       :</em><em><span style="font-family:宋体">日志位置</span>,</em><em><span style="font-family:宋体">同上也在</span>2.3</em><em><span style="font-family:宋体">步骤中</span></em></p> <p> </p> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">启动</span>slave</li> </ol> <pre> <code>mysql> start slave;</code></pre> <p> </p> <h3>3.6<span style="font-family:宋体">查看数据库服务</span>2<span style="font-family:宋体">的</span>master<span style="font-family:宋体">信息</span></h3> <pre> <code>mysql> show master status; +------------------+----------+--------------+------------------+-------------------+ | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------+ | mysql-bin.000001 |      120 | test         |                  |                   | +------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec) mysql></code></pre> <p><img alt="6" class="img-thumbnail" src="/assist/images/blog/8a1bdb08a23d4e6ab2731f9751ba140b.png" /></p> <p> </p> <h2><span style="font-family:宋体">四、配置数据库</span>1master</h2> <ol start="7" style="list-style-type:lower-alpha"> <li value="176">Root<span style="font-family:宋体">用户登录数据库</span>1</li> </ol> <pre> <code>#mysql –uroot –proot</code></pre> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">停止</span>slave</li> </ol> <pre> <code>mysql> stop slave; Query OK, 0 rows affected, 1 warning (0.02 sec) mysql></code></pre> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">配置</span>master<span style="font-family:宋体">指向数据库服务器</span>2</li> </ol> <pre> <code>mysql> change master to       -> master_host='192.168.8.203',      -> master_user='slave',      -> master_password='123456',      -> master_log_file='mysql-bin.000001',      -> master_log_pos=120; Query OK, 0 rows affected, 2 warnings (0.08 sec) 启动slave mysql> start slave; Query OK, 0 rows affected (0.00 sec) </code></pre> <h2><span style="font-family:宋体">五、测试</span></h2> <p> </p> <h3>5.1<span style="font-family:宋体">测试数据库</span>1<span style="font-family:宋体">为</span>master<span style="font-family:宋体">创建表同步至数据库</span>2</h3> <p><span style="font-family:宋体">测试步骤</span>:</p> <ol start="7" style="list-style-type:lower-alpha"> <li value="176">Root<span style="font-family:宋体">数据库用户登录数据库服务器</span>1,<span style="font-family:宋体">切换至</span>test<span style="font-family:宋体">库</span></li> </ol> <p><img alt="7" class="img-thumbnail" src="/assist/images/blog/707f5370f77e4420b0c3cf1ca2279405.png" /></p> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">创建一张表</span>,<span style="font-family:宋体">名称为</span>t_data</li> </ol> <p><img alt="8" class="img-thumbnail" src="/assist/images/blog/9782fe887f6b42668c646d0f83c9e140.png" /></p> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">登录数据库服务器</span>2<span style="font-family:宋体">,切换至数据库</span>test<span style="font-family:宋体">并查看表请情况</span></li> </ol> <p> </p> <p><span style="font-family:宋体">可以看到数据库</span>2<span style="font-family:宋体">中已经存在</span>t_data<span style="font-family:宋体">表格说明表创建已经同步成功</span></p> <p><img alt="9" class="img-thumbnail" src="/assist/images/blog/ac1062d2c431404c986ccd2f70589649.png" /></p> <h3>5.2<span style="font-family:宋体">测试数据库</span>1<span style="font-family:宋体">为</span>master<span style="font-family:宋体">数据内容同步</span></h3> <p><span style="font-family:宋体">测试步骤</span>:</p> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">登录数据库</span>1<span style="font-family:宋体">并切换至</span>test<span style="font-family:宋体">库,新增一条数据到上面创建的</span>t_data<span style="font-family:宋体">表中</span></li> </ol> <p><img alt="10" class="img-thumbnail" src="/assist/images/blog/632dc5d7655d4baf957eb3c6bb49b891.png" /></p> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">登录数据库</span>2<span style="font-family:宋体">,切换至</span>test<span style="font-family:宋体">库查看</span>t_data<span style="font-family:宋体">表数据是否同步</span></li> </ol> <p><img alt="11" class="img-thumbnail" src="/assist/images/blog/a2bf6397291f4986b911acb359fcf51e.png" /></p> <p><span style="font-family:宋体">可以看到数据库</span>2<span style="font-family:宋体">中的表</span>t_data<span style="font-family:宋体">数据内容已经同步。测试数据同步成功。</span></p> <p> </p> <h3>5.3<span style="font-family:宋体">测试数据库</span>2<span style="font-family:宋体">为</span>master<span style="font-family:宋体">创建表同步</span></h3> <p><span style="font-family:宋体">测试步骤</span>:</p> <ol start="7" style="list-style-type:lower-alpha"> <li value="176">Root<span style="font-family:宋体">数据库用户登录数据库</span>2<span style="font-family:宋体">,并切换至</span>test<span style="font-family:宋体">库,创建一个</span>t_data2<span style="font-family:宋体">的表格</span></li> </ol> <p><img alt="12" class="img-thumbnail" src="/assist/images/blog/c2e37210f12c4d33940a40a63a59a80f.png" /></p> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">登录数据库</span>1<span style="font-family:宋体">切换至</span>test<span style="font-family:宋体">库</span>,<span style="font-family:宋体">查看表</span>t_data2<span style="font-family:宋体">是否创建</span></li> </ol> <p><img alt="13" class="img-thumbnail" src="/assist/images/blog/624225953a5d44fca9ff04d18b5caed0.png" /></p> <p><span style="font-family:宋体">可以看到在数据库</span>1<span style="font-family:宋体">中</span>t_data2<span style="font-family:宋体">表已经同步创建,测试表创建同步成功。</span></p> <p> </p> <h3>5.4<span style="font-family:宋体">测试数据库</span>2<span style="font-family:宋体">为</span>master<span style="font-family:宋体">数据内容的同步</span></h3> <p><span style="font-family:宋体">测试步骤</span>:</p> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">登录数据库</span>2<span style="font-family:宋体">,并切换至</span>test<span style="font-family:宋体">库向</span>t_data2<span style="font-family:宋体">表中插入一条数据</span></li> </ol> <p><img alt="14" class="img-thumbnail" src="/assist/images/blog/36ca6b577a124f76a1638df072436855.png" /></p> <ol start="7" style="list-style-type:lower-alpha"> <li value="176"><span style="font-family:宋体">登录数据库</span>1<span style="font-family:宋体">切换至</span>test<span style="font-family:宋体">库查看表</span>t_data2<span style="font-family:宋体">中数据是否同步</span></li> </ol> <p><img alt="15" class="img-thumbnail" src="/assist/images/blog/ba2fb40560134b99b3bd58a8f27ab1ad.png" /></p> <p><span style="font-family:宋体">可以看到数据已经同步至数据库</span>1<span style="font-family:宋体">中的</span>test<span style="font-family:宋体">库的</span>t_data2<span style="font-family:宋体">表。数据同步测试成功。</span></p>
  • AES 使用JavaScript加密然后用Java解密

    引言AES代表高级加密系统,它是一种对称加密算法引言AES代表高级加密系统,它是一种对称加密算法。很多时候我们需要在客户端加密一些明文,例如密码,并将其发送到服务器,然后服务器将其解密以进一步处理.AES加密和解密更容易在Android客户端和Java服务器等相同的平台上实现,但有时在跨平台环境(如Javascript客户端和Java Server,如Spring mvc框架)中解密AES加密密码变得非常具有挑战性,因为如果任何系统默认值不匹配解密将失败。    在本文中,我们将使用spring mvc和angular js客户端创建一个应用程序。我们将有一个带有用户名和密码的表单输入的登录页面。在将密码发送到服务器之前,密码将使用CryptoJS在JavaScript中加密,并且相同的加密密码将在java中解密,并且会进行比较以匹配密码。我们将在javascript中生成salt和IV,然后生成从密码,salt和密钥大小中使用PBKDF2函数的密钥。之后,我们将使用密钥和IV对明文进行加密,并且这些密钥将在Java中进行解密。因此,基本上我们将开发一种可与Java进行可互操作的AES加密的机制,的JavaScript。    在继续进行之前,让我们明确一点,该机制仅在数据的有线传输期间(最有可能)增加了一种额外的安全性,但并未提供充分的证明安全性。如果您不使用SSL,则攻击者可以执行中间人攻击并通过为用户提供不同的密钥来窃取数据。项目结构我们这里有个spring  boot和angular js webapp项目。项目结构如下:​JavaScript中的Aes加密    对于JavaScript中的AES加密,我们已经导入了两个js文件 - crypto.js并且pbkdf2.js我们有AesUtil.js用于执行加密和解密的通用代码。这里this.keySize是4字节块的密钥大小。因此,要使用128位密钥,我们将位数除以32得到用于CryptoJS的密钥大小。 AesUtil.js:var AesUtil = function(keySize, iterationCount) { this.keySize = keySize / 32; this.iterationCount = iterationCount; }; AesUtil.prototype.generateKey = function(salt, passPhrase) { var key = CryptoJS.PBKDF2( passPhrase, CryptoJS.enc.Hex.parse(salt), { keySize: this.keySize, iterations: this.iterationCount }); return key; } AesUtil.prototype.encrypt = function(salt, iv, passPhrase, plainText) { var key = this.generateKey(salt, passPhrase); var encrypted = CryptoJS.AES.encrypt( plainText, key, { iv: CryptoJS.enc.Hex.parse(iv) }); return encrypted.ciphertext.toString(CryptoJS.enc.Base64); } AesUtil.prototype.decrypt = function(salt, iv, passPhrase, cipherText) { var key = this.generateKey(salt, passPhrase); var cipherParams = CryptoJS.lib.CipherParams.create({ ciphertext: CryptoJS.enc.Base64.parse(cipherText) }); var decrypted = CryptoJS.AES.decrypt( cipherParams, key, { iv: CryptoJS.enc.Hex.parse(iv) }); return decrypted.toString(CryptoJS.enc.Utf8); }密码加密在JavaScript中    该方法logMeIn()将在点击提交按钮后被调用。此方法将使用定义的通用代码AesUtil.js对密码进行加密并使POST请求验证密码。发送的密码将采用以下格式:iv::salt::ciphertext在服务器端,java将解密密码并在响应中发送解密密码显示在警告框中。var app = angular.module('demoApp', []); app.controller('loginController', ['$scope', '$rootScope', '$http', function ($scope, $rootScope, $http) { $scope.logMeIn = function(){ if(!$scope.userName || !$scope.password){ $scope.showMessage("Missing required fields.", false); return; } var iv = CryptoJS.lib.WordArray.random(128/8).toString(CryptoJS.enc.Hex); var salt = CryptoJS.lib.WordArray.random(128/8).toString(CryptoJS.enc.Hex); var aesUtil = new AesUtil(128, 1000); var ciphertext = aesUtil.encrypt(salt, iv, $('#key').text(), $scope.password); var aesPassword = (iv + "::" + salt + "::" + ciphertext); var password = btoa(aesPassword); var data = { userName: $scope.userName, password: password } $http.post('/login',data).then(function (response){ if(response.status === 200){ alert("Password is " + response.data.password); }else { alert("Error occurred"); } }) }; }]);Java中的AES解密首先让我们实现将拦截登录请求的控制器类。在这里,我们对密钥进行了硬编码。此密钥将由服务器唯一地生成并发送给客户端以用于每个登录请求。客户端将使用相同的密钥,而加密和服务器将使用相同的密钥进行解密。确保密钥长度为16,因为我们使用的是128位加密。请记住我们从客户端发送的加密文本的格式iv::salt::ciphertext。文本以相同的格式解密。我们已经有IV,盐和密文。import com.example.demo.model.Credentials; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; import java.util.UUID; @Controller public class WelcomeController { private static final Logger LOGGER = LoggerFactory.getLogger(WelcomeController.class); @RequestMapping(value={"/login"},method = RequestMethod.GET) public String loginPage(HttpServletRequest request){ LOGGER.info("Received request for login page with id - " + request.getSession().getId()); String randomKey = UUID.randomUUID().toString(); //String uniqueKey = randomKey.substring(randomKey.length()-17, randomKey.length() -1); String uniqueKey = "1234567891234567"; request.getSession().setAttribute("key", uniqueKey); return "index"; } @RequestMapping(value={"/login"},method = RequestMethod.POST) public @ResponseBody ResponseEntity login(@RequestBody Credentials credentials, HttpServletRequest request) { String decryptedPassword = new String(java.util.Base64.getDecoder().decode(credentials.getPassword())); AesUtil aesUtil = new AesUtil(128, 1000); Map map = new HashMap<>(); if (decryptedPassword != null && decryptedPassword.split("::").length == 3) { LOGGER.info("Password decrypted successfully for username - " + credentials.getUserName()); String password = aesUtil.decrypt(decryptedPassword.split("::")[1], decryptedPassword.split("::")[0], "1234567891234567", decryptedPassword.split("::")[2]); map.put("password", password); } return new ResponseEntity<>(map, HttpStatus.OK); } } 以下是用于AES加密和解密的java util类。您可以按照java中的AES加密和解密获得关于以下实现的更多详细说明。import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Hex; import javax.crypto.*; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; public class AesUtil { private final int keySize; private final int iterationCount; private final Cipher cipher; public AesUtil(int keySize, int iterationCount) { this.keySize = keySize; this.iterationCount = iterationCount; try { cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { throw fail(e); } } public String decrypt(String salt, String iv, String passphrase, String ciphertext) { try { SecretKey key = generateKey(salt, passphrase); byte[] decrypted = doFinal(Cipher.DECRYPT_MODE, key, iv, base64(ciphertext)); return new String(decrypted, "UTF-8"); } catch (UnsupportedEncodingException e) { return null; }catch (Exception e){ return null; } } private byte[] doFinal(int encryptMode, SecretKey key, String iv, byte[] bytes) { try { cipher.init(encryptMode, key, new IvParameterSpec(hex(iv))); return cipher.doFinal(bytes); } catch (InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { return null; } } private SecretKey generateKey(String salt, String passphrase) { try { SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), hex(salt), iterationCount, keySize); SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES"); return key; } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { return null; } } public static byte[] base64(String str) { return Base64.decodeBase64(str); } public static byte[] hex(String str) { try { return Hex.decodeHex(str.toCharArray()); } catch (DecoderException e) { throw new IllegalStateException(e); } } private IllegalStateException fail(Exception e) { return null; } }测试AES加密和解密     DemoApplication.java作为Java应用程序运行,然后打到http:// localhost:8080。一旦出现登录页面,您可以输入用户名和密码,然后单击提交按钮,您可以在警报中看到解密的密码。​总结在这篇文章中,我们讨论了使用Java和Javascript进行可互操作的AES加密。我们使用Crypto.js库在javascript中执行此加密。提示:项目源码下载aes-encryption-javascript-java-master.zip
  • spring boot shiro 无状态token认证

    一、spring boot shiro 无状态token认证项目结构图​二、无状态spring boot shiro相关配置2.1shiro redis 缓存配置首先是实现shiro的cache接口$title(RedisCache.jav一、spring boot shiro 无状态token认证项目结构图​二、无状态spring boot shiro相关配置2.1shiro redis 缓存配置首先是实现shiro的cache接口$title(RedisCache.java) package net.xqlee.project.demo.shiro.config.shiro.cache; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.core.RedisTemplate; /** * Redis的Shiro缓存对象实现 * * @author xq * * @param <K> * @param <V> */ public class RedisCache<K, V> implements Cache<K, V> { private static final Logger logger = LoggerFactory.getLogger(RedisCache.class); private RedisTemplate<K, V> redisTemplate; private final static String PREFIX = "shiro-cache:"; private String cacheKey; private long globExpire = 30; @SuppressWarnings({ "rawtypes", "unchecked" }) public RedisCache(final String name, final RedisTemplate redisTemplate) { this.cacheKey = PREFIX + name + ":"; this.redisTemplate = redisTemplate; } @Override public V get(K key) throws CacheException { logger.debug("Shiro从缓存中获取数据KEY值["+key+"]"); redisTemplate.boundValueOps(getCacheKey(key)).expire(globExpire, TimeUnit.MINUTES); return redisTemplate.boundValueOps(getCacheKey(key)).get(); } @Override public V put(K key, V value) throws CacheException { V old = get(key); redisTemplate.boundValueOps(getCacheKey(key)).set(value); return old; } @Override public V remove(K key) throws CacheException { V old = get(key); redisTemplate.delete(getCacheKey(key)); return old; } @Override public void clear() throws CacheException { redisTemplate.delete(keys()); } @Override public int size() { return keys().size(); } @Override public Set<K> keys() { return redisTemplate.keys(getCacheKey("*")); } @Override public Collection<V> values() { Set<K> set = keys(); List<V> list = new ArrayList<>(); for (K s : set) { list.add(get(s)); } return list; } @SuppressWarnings("unchecked") private K getCacheKey(Object k) { return (K) (this.cacheKey + k); } } 接下来是实现shiro的CacheManager$title(RedisCacheManager.java) package net.xqlee.project.demo.shiro.config.shiro.cache; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheException; import org.apache.shiro.cache.CacheManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; /** * Redis的Shiro缓存管理器实现 * * @author xq * */ public class RedisCacheManager implements CacheManager { @Autowired private RedisTemplate<String, Object> redisTemplate; @Override public <K, V> Cache<K, V> getCache(String name) throws CacheException { return new RedisCache<>(name, redisTemplate); } } shiro的缓存配置就是以上两个文件了。至于redis的配置这里就不讲述了。2.2自定义一个AuthenticationToken实现 自定义一个AuthenticationToken实现用于登录认证等。$title(StatelessAuthenticationToken.java) package net.xqlee.project.demo.shiro.config.shiro; import org.apache.shiro.authc.AuthenticationToken; import java.util.Map; /** * 自定义实现Shiro的一个认证Token * * @author xq */ public class StatelessAuthenticationToken implements AuthenticationToken { /** * */ private static final long serialVersionUID = 1L; private String token;// 用户登录后获取的令牌; // 构造函数 public StatelessAuthenticationToken() { } public StatelessAuthenticationToken(String token) { this.token = token; } @Override public Object getPrincipal() { return token; } @Override public Object getCredentials() { return this; } } 这个类其实很简单。就是实现接口并添加了一个token的字段。用于后面进行认证。2.3 自定义shiro的Realm$title(UserRealm.java) package net.xqlee.project.demo.shiro.config.shiro; import net.xqlee.project.demo.shiro.pojo.LoginAccount; import net.xqlee.project.demo.shiro.service.UserService; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.util.List; /** * 实现一个基于JDBC的Realm,继承AuthorizingRealm可以看见需要重写两个方法,doGetAuthorizationInfo和doGetAuthenticationInfo * * @author xqlee */ @Component public class UserRealm extends AuthorizingRealm { private static final Logger logger = LoggerFactory.getLogger(UserRealm.class); /*** 用户业务处理类,用来查询数据库中用户相关信息 ***/ @Autowired UserService userService; /** * 该Realm仅支持自定义的StatelessAuthenticationToken类型Token,其他类型处理将会抛出异常 * * @param token * @return */ @Override public boolean supports(AuthenticationToken token) { return token instanceof StatelessAuthenticationToken; } /*** * 获取用户授权 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { logger.info("##################执行Shiro权限认证##################"); // 获取用户名 LoginAccount account = (LoginAccount) principalCollection.getPrimaryPrincipal(); // 判断用户名是否存在 if (StringUtils.isEmpty(account)) { throw new RuntimeException("获取用户授权信息失败"); } // 创建一个授权对象 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 进行权限设置 List<String> permissions = account.getPermissions(); if (permissions != null && !permissions.isEmpty()) { info.addStringPermissions(permissions); } // 角色设置 List<String> roles = account.getRoles(); if (roles != null) { info.addRoles(roles); } return info; } /** * 获取用户认证信息 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { logger.info("##################执行Shiro登陆认证##################"); StatelessAuthenticationToken statelessAuthenticationToken = (StatelessAuthenticationToken) authenticationToken; // 通过表单接收的用户名 String token = (String)statelessAuthenticationToken.getPrincipal(); if (StringUtils.isEmpty(token)) { throw new UnknownAccountException("token无效"); } // 根据token获取用户信息 LoginAccount account = userService.getAccountByToken(token); if (account == null) { throw new UnknownAccountException("token无效"); } // 创建shiro的用户认证对象 // 注意该对象的密码将会传递至后续步骤与前面登陆的subject的密码进行比对。 //这里放入account对象后面授权可以取出来 //statelessAuthenticationToken会与登录时候的token进行验证这里就放入登录的即可 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(account, statelessAuthenticationToken, getName()); return authenticationInfo; } } 这个类主要用于shiro的登录认证以及权限认证。2.4、重写shiro的DefaultWebSubjectFactory重写DefaultWebSubjectFactory主要是关闭创建session$title(StatelessDefaultSubjectFactory.java) package net.xqlee.project.demo.shiro.config.shiro; import org.apache.shiro.subject.Subject; import org.apache.shiro.subject.SubjectContext; import org.apache.shiro.web.mgt.DefaultWebSubjectFactory; /** * Subject工厂重写 * * @author xq * */ public class StatelessDefaultSubjectFactory extends DefaultWebSubjectFactory { @Override public Subject createSubject(SubjectContext context) { // 不创建session. context.setSessionCreationEnabled(false); return super.createSubject(context); } } 2.5 创建一个代理登录的filter这个filter是无状态的重点之一$title(StatelessAccessControlFilter.java) package net.xqlee.project.demo.shiro.config.shiro; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.web.filter.AccessControlFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; /** * 该过滤器需在shiro配置类中加入filter链 */ public class StatelessAccessControlFilter extends AccessControlFilter { private static final Logger logger = LoggerFactory.getLogger(StatelessAccessControlFilter.class); @Value("${token.name}") String tokenName; /** * 先执行:isAccessAllowed 再执行onAccessDenied * <p> * isAccessAllowed:表示是否允许访问;mappedValue就是[urls]配置中拦截器参数部分, * 如果允许访问返回true,否则false; * <p> * 如果返回true的话,就直接返回交给下一个filter进行处理。 如果返回false的话,回往下执行onAccessDenied */ @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { return false; } /** * onAccessDenied:表示当访问拒绝时是否已经处理了;如果返回true表示需要继续处理; * 如果返回false表示该拦截器实例已经处理了,将直接返回即可。 */ @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { //==============================该步骤主要是通过token代理登录shiro====================== //获取参数中的token值 String token = request.getParameter(tokenName);//这里取的参数中的token你也可以将token放于head等 // 生成无状态Token然后代理登录 StatelessAuthenticationToken statelessAuthenticationToken = new StatelessAuthenticationToken(token); try { // 委托给Realm进行登录 getSubject(request, response).login(statelessAuthenticationToken); } catch (UnknownAccountException ue) { logger.debug(ue.getLocalizedMessage()); } catch (Exception e) { logger.error(e.getLocalizedMessage(), e); //登录失败不用处理后面的过滤器会处理并且能通过@ControllerAdvice统一处理相关异常 } return true; } } 2.6shiro的核心配置$title(ShiroConfig.java) package net.xqlee.project.demo.shiro.config.shiro; import java.util.LinkedHashMap; import java.util.Map; import org.apache.shiro.SecurityUtils; import org.apache.shiro.cache.CacheManager; import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.mgt.DefaultSessionStorageEvaluator; import org.apache.shiro.mgt.DefaultSubjectDAO; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.mgt.DefaultSessionManager; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import net.xqlee.project.demo.shiro.config.shiro.cache.RedisCacheManager; /*** * * @author xqlee * */ @Configuration public class ShiroConfig { /** * ehcache缓存方案<br/> * 简单的缓存,后续可更换为redis缓存,通过自己实现shiro的CacheManager接口和Cache接口 * * @return */ @Bean public CacheManager shiroEhCacheManager() { EhCacheManager cacheManager = new EhCacheManager(); cacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml"); return cacheManager; } /** * redis缓存方案 * * @return */ @Bean public CacheManager shiroRedisCacheManager() { return new RedisCacheManager(); } /**** * 自定义Real * * @return */ @Bean public UserRealm userRealm() { UserRealm realm = new UserRealm(); // 根据情况使用缓存器 realm.setCacheManager(shiroRedisCacheManager());// shiroEhCacheManager() return realm; } /** * 自定义的无状态(无session)Subject工厂 * * @return */ @Bean public StatelessDefaultSubjectFactory subjectFactory() { return new StatelessDefaultSubjectFactory(); } /** * sessionManager通过sessionValidationSchedulerEnabled禁用掉会话调度器, * 因为我们禁用掉了会话,所以没必要再定期过期会话了。 * * @return */ @Bean public DefaultSessionManager sessionManager() { DefaultSessionManager sessionManager = new DefaultSessionManager(); sessionManager.setSessionValidationSchedulerEnabled(false); //缓存 sessionManager.setCacheManager(shiroRedisCacheManager()); return sessionManager; } /*** * 安全管理配置 * * @return */ @Bean public SecurityManager defaultWebSecurityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 注意这里必须配置securityManager SecurityUtils.setSecurityManager(securityManager); // 根据情况选择缓存器 securityManager.setCacheManager(shiroRedisCacheManager());// shiroEhCacheManager() // 设置Subject工厂 securityManager.setSubjectFactory(subjectFactory()); // 配置 securityManager.setRealm(userRealm()); // session securityManager.setSessionManager(sessionManager()); // 禁用Session作为存储策略的实现。 DefaultSubjectDAO defaultSubjectDAO = (DefaultSubjectDAO) securityManager.getSubjectDAO(); DefaultSessionStorageEvaluator defaultSessionStorageEvaluatord = (DefaultSessionStorageEvaluator) defaultSubjectDAO .getSessionStorageEvaluator(); defaultSessionStorageEvaluatord.setSessionStorageEnabled(false); return securityManager; } /** * 配置shiro的拦截器链工厂,默认会拦截所有请求 * * @return */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean() { ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean(); // 配置安全管理(必须) filterFactoryBean.setSecurityManager(defaultWebSecurityManager()); //配置自定义Filter filterFactoryBean.getFilters().put("statelessAuthc", statelessAuthcFilter()); // 配置登陆的地址 filterFactoryBean.setLoginUrl("/userNoLogin.do");// 未登录时候跳转URL,如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 filterFactoryBean.setSuccessUrl("/welcome.do");// 成功后欢迎页面 filterFactoryBean.setUnauthorizedUrl("/403.do");// 未认证页面 // 配置拦截地址和拦截器 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();// 必须使用LinkedHashMap,因为拦截有先后顺序 // statelessAuthc:所有url都必须认无状态证通过才可以访问; anon:所有url都都可以匿名访问 filterChainDefinitionMap.put("/userNoLogin.do**", "anon");// 未登录跳转页面不设权限认证 filterChainDefinitionMap.put("/login.do**", "anon");// 登录接口不设置权限认真 filterChainDefinitionMap.put("/logout.do**", "anon");// 登出不需要认证 // 下面的的其他资源地址全部需要通过代理登录步骤,注意顺序,必须先进过无状态代理登录后,后面的权限和角色认证才能使用 filterChainDefinitionMap.put("/**", "statelessAuthc"); filterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); // 全部配置 // anon org.apache.shiro.web.filter.authc.AnonymousFilter 匿名访问 // // authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter // 需要登录,不需要权限和角色可访问 // // authcBasic // org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter // // perms // org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter // 需要给定的权限值才能访问 // // port org.apache.shiro.web.filter.authz.PortFilter // // rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter // // roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter // 需要给定的角色才能访问 // // ssl org.apache.shiro.web.filter.authz.SslFilter // // user org.apache.shiro.web.filter.authc.UserFilter // // logout org.apache.shiro.web.filter.authc.LogoutFilter return filterFactoryBean; } /** * Add.4.1 访问控制器. * * @return */ @Bean public StatelessAccessControlFilter statelessAuthcFilter() { StatelessAccessControlFilter statelessAuthcFilter = new StatelessAccessControlFilter(); return statelessAuthcFilter; } /** * Add.5.1 开启shiro aop注解支持. 使用代理方式;所以需要开启代码支持; * * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * Add.5.2 自动代理所有的advisor: 由Advisor决定对哪些类的方法进行AOP代理。 */ @Bean public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); return defaultAdvisorAutoProxyCreator; } } 三、无状态访问资源创建3.1 shiro登录$title(LoginController.java) package net.xqlee.project.demo.shiro.controller; import net.xqlee.project.demo.shiro.pojo.LoginAccount; import net.xqlee.project.demo.shiro.pojo.Result; import net.xqlee.project.demo.shiro.service.UserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * 用户登录用 * * @author xqlee */ @RestController public class LoginController { private static final Logger logger = LoggerFactory.getLogger(LoginController.class); @Autowired UserService userService; /**** * 用户未登录 * * @return */ @GetMapping("userNoLogin.do") public Object noLogin() { return Result.noLogin(); } @GetMapping(value = "/login.do") public Object login(String loginName, String password) { try { LoginAccount account = userService.getLoginAccountByLoginName(loginName); if (account == null) { return Result.fail("账号不存在"); } if (!account.getPassword().equals(password)) { return Result.fail("密码错误"); } if (!account.isEnabled()) { return Result.fail("账号被锁"); } String token = userService.createToken(account); return Result.success(token); } catch (Exception e) { return Result.fail(e.getMessage()); } } } 这个类主要是用于登录然后将登录成功的token返回给请求者。3.2下面就是一个测试的资源访问了$title(ResourcesController.java) package net.xqlee.project.demo.shiro.controller; import net.sf.json.JSONObject; import net.xqlee.project.demo.shiro.pojo.Result; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresRoles; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; @RestController public class ResourcesController { @RequiresPermissions("user:h")//需要用户拥有user:h权限 @GetMapping("/user/hello.do") public String hello() { return "Hello User, From Server"; } @GetMapping("/hello.do") @RequiresRoles(value = "ROLE_USER")//需要用户拥有ROLE_USER角色 public String hello2() { return "Hello User2,Form Server"; } @RequiresPermissions("admin:h")//需要用户拥有admin:h权限 @GetMapping("/admin/hello.do") public String helloAdmin() { return "Hello Admin, From Server"; } @GetMapping("/welcome.do")// public String loginSuccess() { return "welcome"; } @GetMapping("/403.do") public Object error403(HttpServletResponse response) { response.setStatus(403); return Result.noPermission(); } } 四、测试无状态shiro访问 4.1不登录直接访问访问无需权限和无需认证的/welcome.do​可以看到成功访问到了无需认证和授权的接口。接下来访问需要权限的接口:​  可以看到返回了用户未登录的信息4.2 用户登录后访问 首先是用户登录:​用户登录并返回token 可以看到成功返回了token也就是data字段通过token访问需要普通用户权限的接口:​访问授权接口 可以看到上方已经成功访问了需要普通用户权限的接口。接下来我们访问该用户未有权限的admin相关接口:​访问没有权限的接口 上方可以看到,返回了权限不足的提示。好啦到这里spring boot shiro无状态化已经完成。有何疑问或者更好的建议或者需要项目源码的欢迎留言反馈。源码下载:点击前往下载源码