网站配置使用https访问
简单了解HTTPS、HTTP、SSL、TLS
-
http的连接很简单,是无状态的,无加密,也就是明文,数据都可以看到。
-
HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
-
SSL是基于HTTP之下TCP之上的一个协议层,是基于HTTP标准并对TCP传输数据时进行加密,所以HPPTS是HTTP+SSL/TCP的简称。
-
TLS(传输层安全)是更为安全的升级版 SSL。
IIS配置为HTTPS方式访问
访问https链接的时候可能会出现“不安全的链接”的错误提示,或者会提示“未签名的数字证书”。
不要慌!单击继续访问或者另一种报错下方有个高级选项,继续访问即可。IIS证书默认有效时间是一年哦。
Tomcat配置为HTTPS访问
首先,网站要能够正常访问,需要配置JDK环境和Tomcat的环境变量,教程很多不多说。
进入JDK安装目录,以自己的目录为准,可以看到keytool工具,JDK中keytool是一个证书管理工具,可以生成自签名证书(即自己生成的证书,并不是官方生成的证书)。除非是很正式的项目,否则使用自己签发的证书即可,因为官方生成证书收费哟,在此目录空白位置按住shift键+鼠标右键,在此处打开命令窗口,或者直接运行cmd,输入如下命令:
cd C:\Program Files\Java\jdk-11.0.2\bin
使用keytool生成免费证书,默认有效时间是3个月,可以通过**-validity**设置有效时间:
keytool -genkey -alias tomcat -keyalg RSA -keystore C:\tomcat.keystore
注意:上面***你的名字与姓氏***使用你的域名或者IP,本地既作服务端又作客户端时,可以使用localhost,如果要测试在局域网访问则使用IP,当然有域名就使用域名,外网域名大家都可以访问。通过浏览器访问时就可以用https://192.168.1.111:8443这样的方式去访问。
查看证书
keytool -list -v -keystore C:\tomcat.keystore
配置tomcat
修改apache-tomcat-8.5.39\conf目录下的server.xml。
1、注释掉8080端口配置(可选,不注释的话使用http的8080端口也可以访问)
2、添加8443端口配置,也可以用443端口(443是https默认端口,通过浏览器访问时可以不写端口),将生成的证书路径和密码配置到
<Connector protocol="org.apache.coyote.http11.Http11NioProtocol"
port="8443" maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
keystoreFile="C:\tomcat.keystore"
keystorePass="123456"
clientAuth="false" sslProtocol="TLS"/>
属性说明:
clientAuth:设置是否双向验证,默认为false,设置为true代表双向验证
keystoreFile:服务器证书文件路径
keystorePass:服务器证书密码
3、浏览器访问,输入https://localhost:8443/
这个时候提示证书错误,因为客户端没有信任服务端证书,步骤参考双向认证的第5、6步,安装证书之后就不会再提示了。
配置tomcat为双向认证
上面已经配置了单向认证,接下来配置双向认证。在前述配置的基础上进行配置。先简单介绍下SSL双向认证机制:
客户端(通常是浏览器)带上自己的客户端证书(私钥,第7步)去请求服务器,服务器由于信任了客户端证书(第4步),那服务器收到客户端的证书,自然就能够解析出来,我认识你。然后就把我的宝贝(服务端证书,即公钥)返回给客户端,客户端又信任了服务端的证书(第6步),我也认识你,既然彼此认识呢。那咱们就可以说点悄悄话了。双向认证完成。其他人都不知道你的私钥和公钥,就算把你的请求都抓下来,也无法解析其中的数据。这就是https的这个"S",安全!
属性说明:
truststoreFile:用来验证客户端证书的根证书,此例中就是服务器证书
truststorePass:根证书密码
1、修改server.xml文件,添加上述两个属性,并将clientAuth设置为true
<Connector protocol="org.apache.coyote.http11.Http11NioProtocol"
port="8443" maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
keystoreFile="C:\tomcat.keystore"
keystorePass="123456"
truststoreFile="C:\tomcat.keystore"
truststorePass="123456"
clientAuth="true" sslProtocol="TLS"/>
2、服务端证书库已经在前文生成,现在生成客户端证书库
keytool -genkey -alias client -keypass 123456 -keyalg RSA -storetype PKCS12 -storepass 123456 -keystore C:\client.p12
3、导出客户端证书
keytool -export -alias client -keystore C:\client.p12 -storetype PKCS12 -keypass 123456 -file C:\client.cer
4、将生成的客户端证书导入到服务器的证书库,添加为一个信任证书。
keytool -import -v -file C:\client.cer -keystore C:\tomcat.keystore
添加之后再使用命令查看服务器证书库,可以看到两个证书信息。一个是服务器证书,一个是受信任的客户端证书。截图就省略了,太长了。
keytool -list -v -keystore C:\tomcat.keystore
5、导出服务端证书
keytool -keystore C:\tomcat.keystore -export -alias tomcat -file C:\server.cer
6、将服务端证书导入到客户端浏览器受信任的根证书中。
Internet选项>>内容>>证书>>受信任的根证书颁发机构>>导入,选择刚刚导出的服务端证书,或者直接双击server.cer安装证书,两种安装证书方式有一丢丢差异,但是最终结果是一样的,相信难不倒聪明的你。
7、客户端浏览器安装客户端证书库。
安装之后,客户端浏览器请求的时候就会带上客户端证书(私钥)去访问服务器。
安装过程跟上面服务器安装过程类似,只不过这次我们需要将客户端证书库安装到"个人"下,需要输入之前设置的客户端证书库密码。
双击client.p12进行安装
也可以直接选择"根据证书类型,自动选择证书存储"。
双向认证完成,再也没有之前的证书错误提示了,真香!!!还不赶紧打开网页试一试!!!
浏览器输入https://192.168.1.111:8443,每次浏览器访问都需要手动确认发送客户端证书。
确定之后正常打开网页
强制http跳转到https,首先修改你的apache-tomcat-8.5.39\conf目录下的web.xml
在最后添加:
<!-- 强制SSL配置,即普通的请求也会重定向为SSL请求 -->
<security-constraint>
<web-resource-collection>
<web-resource-name>SSL</web-resource-name>
<url-pattern>/*</url-pattern><!-- 全站使用SSL -->
</web-resource-collection>
<user-data-constraint>
<description>SSL required</description>
<!-- CONFIDENTIAL: 要保证服务器和客户端之间传输的数据不能够被修改,且不能被第三方查看到 -->
<!-- INTEGRAL: 要保证服务器和client之间传输的数据不能够被修改 -->
<!-- NONE: 指示容器必须能够在任一的连接上提供数据。(即用HTTP或HTTPS,由客户端来决定)-->
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
是不是以为到这就万事大吉了?NO!NO!NO!
还记不记得前面说注释掉8080端口,也可以不注释,那你到底注释了没有呢?如果你注释了,那你有其他的http端口开着吗,是否重定向到8443端口了?没有的话,配置强制访问有什么意义。所以我就是在坑你!!!简单说一句,你设置了强制重定向,那么你就需要在server.xml配置你需要跳转的http端口,并设置redirectPort=“8443”,也就是说将你指定的http端口重定向到你的https端口。比如前面我不注释我的8080端口并重定向到了8443。那么我使用http://192.168.1.111:8080访问时就自动跳转到了https://192.168.1.111:8443。
tomcat对webservice进行权限验证
WebService鉴权方式以下几种:
1、对Web方法添加用户名和密码两个参数,在实现类中进行对其权限验证。
2、使用SoapHeader,将用户名和密码绑定到SoapHeader,在服务端添加拦截器,对SoapHeader进行拦截,获取其中的用户名密码进行权限验证。
这两种方式都是对客户端调用方法时的验证,还是能访问wsdl文件,如果要对wsdl文件的访问也加上用户权限验证,添加验证之后,wsdl地址的访问也需要用户明和密码才能访问,有以下两种方法:
方法一:
这里注意修改xml文件时使用的工具,用notepad++可以设置编码格式,那么你的中文注释使用UTF-8编码就能够识别,如果使用普通的记事本的话不能更改编码格式,部署tomcat会报错,xml文件解析不出来,虽然成功启动tomcat,但是在启动的时候它明确提示你这是严重的错误,当然你的URL肯定也是访问不了。所以最简单的方式就是在正式环境就别用中文注释了。
1、修改web.xml,添加如下:
<security-constraint>
<web-resource-collection>
<http-method>GET</http-method>
<http-method>POST</http-method>
<web-resource-name>tomcat protect page</web-resource-name>
<!-- /soap目录下的所有资源是受保护的 -->
<!-- 我的WebService地址/soap/CommonService?wsdl -->
<!-- url-pattern不能写/CommonService,必须写它的上一级目录,否则无法访问 -->
<url-pattern>/soap/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<!-- 这里的myUserRole要与tomcat-user.xml中配置的role一致 -->
<role-name>myUserRole</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<!-- 验证方式,可选的值为: "BASIC", "DIGEST", "FORM", "CLIENT-CERT" -->
<auth-method>BASIC</auth-method>
<!-- 使用的Realm名字,注意这里不能有空格 -->
<!-- 在客户端调用时使用类似setRealm("WEBSERVICELOGIN")函数时会用这个名字 -->
<realm-name>WEBSERVICELOGIN</realm-name>
</login-config>
<security-role>
<description>myUserRole</description>
<role-name>myUserRole</role-name>
</security-role>
如果不加最后security-role属性里面的内容,启动tomcat可能会出现警告:
WARNING [Catalina-utility-2] org.apache.catalina.startup.ContextConfig.validateSecurityRoles Security role name [myUserRole] used in an <auth-constraint> without being defined in a <security-role>
2、修改tomcat-user.xml
<role rolename="myUserRole"/>
<user username="myUser" password="myPassword" roles="myUserRole"/>
重启tomcat
客户端在访问时使用setUsername()和setPassword()两个函数传递用户名和密码进行验证,根据自己的客户端的确定调用的方法,基本上都是这两个函数。这个账号密码是在http协议头的authorization属性里面,不在SOAP协议的SoapHeader里面,切勿混淆,解密出来就是用户名和密码:
使用idea选择Java->Web Service Client创建基于axis的客户端调用:
((HelloServiceSoapBindingStub) service).setUsername("myUser");
((HelloServiceSoapBindingStub) service).setPassword("myPassword");
也可以不创建客户端,动态调用:
call.setUsername("myUser");
call.setPassword("myPassword");
方法二:
本方法基于springboot整合shiro框架,自己对shiro封装一个ShiroConfig配置类,主要代码如下:
/**
* Shiro过滤器配置
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager)
{
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// Shiro的核心安全接口,这个属性是必须的
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 身份认证失败,则跳转到登录页面的配置
shiroFilterFactoryBean.setLoginUrl(loginUrl);
// 权限认证失败,则跳转到指定页面
shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
// Shiro连接约束配置,即过滤链的定义
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// 对静态资源设置匿名访问,shiro按顺序验证
filterChainDefinitionMap.put("/excelTemp/**", "anon");
filterChainDefinitionMap.put("/favicon.ico**", "anon");
filterChainDefinitionMap.put("/wootion.png**", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/docs/**", "anon");
filterChainDefinitionMap.put("/fonts/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/ajax/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/wootion/**", "anon");
filterChainDefinitionMap.put("/druid/**", "anon");
filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");
//我的WebService
filterChainDefinitionMap.put("/soap/**", "anon");
// 退出 logout地址,shiro去清除session
// 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了,登出后跳转配置的loginUrl
filterChainDefinitionMap.put("/logout", "logout");
// 不需要拦截的访问
//filterChainDefinitionMap.put("/login", "ssl,anon,captchaValidate");
filterChainDefinitionMap.put("/login", "anon,captchaValidate");
// 系统权限列表
// filterChainDefinitionMap.putAll(SpringUtils.getBean(IMenuService.class).selectPermsAll());
Map<String, Filter> filters = new LinkedHashMap<>();
filters.put("onlineSession", onlineSessionFilter());
filters.put("syncOnlineSession", syncOnlineSessionFilter());
filters.put("captchaValidate", captchaValidateFilter());
// 注销成功,则跳转到指定页面
filters.put("logout", logoutFilter());
//filters.put("ssl",sslFilter());
shiroFilterFactoryBean.setFilters(filters);
// 所有请求需要认证
filterChainDefinitionMap.put("/**", "user,onlineSession,syncOnlineSession");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
这里面主要就是下面这句:
//我的WebService
filterChainDefinitionMap.put("/soap/**", "anon");
对我的webservice:/soap/的所有url进行拦截,这里使用的是anon匿名访问,也就是无验证,修改成authc就必须要求用户在访问时先在我的登录页面进行登录,否则无法访问。
使用tomcat时遇到的一些问题总结
1、要想使用tomcat8w.exe,需要注册服务否则提示指定服务未安装,在tomcat的bin目录运行命令service.bat install即可,注意版本问题,和下面一样,注册服务会绑定系统的java环境,在jdk1.8注册的服务,部署JDK11的项目会报错,修改系统环境变量还不行,还要删除服务重新在新环境下注册,删除服务使用sc delete+服务名。
2、tomcat出现部署失败,注意检查tomcat版本与JDK版本是否兼容(基础不牢,导致我犯下这个错误),首先是确认本身的运行环境tomcat与java版本的兼容性,其次是检查自己项目所使用的的java版本与当前系统环境变量设置的Java版本(如果自己有多个版本JDK),我有两个JDK版本分别对应两个不同的项目,一个用JDK1.8,一个用JDK11,我在系统环境变量里配置使用的JDK1.8,但是我启动使用JDK11的项目就会导致网页无法访问甚至报错,修改环境变量为JDK11恢复正常,同理,我在JDK11环境下启动使用JDK1.8的项目就会报部署的错,报错类似:
严重 [localhost-startStop-1] org.apache.catalina.core.ContainerBase.addChildInternal ContainerBase.addChild: start:
org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/roadgate]]
严重 [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Error deploying web application archive C:\Users\Administrator\Desktop\apache-tomcat-8.0.53\webapps\roadgate.war
java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/roadgate]]
tomcat与所支持的Java版本对照表
3、IDEA出现编码问题,网上方法一大堆,也没搞明白,其实根本原因就是你各个源文件的编码不一致,有可能有的文件是你直接复制的别人的,而别人并没有使用你这种编码格式。解决办法最简单的就是把所有格式不一致的源文件使用notepad++(用什么工具修改看你心情,不固定)修改为统一的格式。
4、缓存警告:
WARNING [localhost-startStop-1] org.apache.catalina.webresources.Cache.getResource Unable to add the resource at [/WEB-INF/classes/static/excelTemp/20201102/LVSHCAAU6LE048629Ticket.xls] to the cache for web application [/roadgate] because there was insufficient free space available after evicting expired cache entries - consider increasing the maximum size of the cache
tomcat默认允许缓存且大小为10M,用于加载静态资源,如js、css、静态页面等,缓存大小根据自己的需要设置,在 tomcat/conf/context.xml 文件中增加如下内容:
<Resources cachingAllowed="true" cacheMaxSize="100000" />
本文所涉及源码下载
项目完整代码(含服务端及客户端)
客户端代码,方便没有C币的朋友参考:
package example;
import com.HelloServiceSoapBindingStub;
import com.HelloService_PortType;
import com.HelloService_ServiceLocator;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.axis.encoding.XMLType;
import org.apache.axis.message.SOAPHeaderElement;
import javax.management.remote.rmi.RMIConnection;
import javax.xml.namespace.QName;
import javax.xml.rpc.ParameterMode;
import javax.xml.rpc.ServiceException;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
//创建Java项目,勾选Web Service Client,选择Apache axis版本,创建后自动弹窗创建接口相关类及wsdl文件(com包下,包名自己取)
//基于axis框架的WebService客户端,还有基于cxf的
public class HelloWorldClient {
public static void main(String[] argv) {
clientCall();
dynamicCall();
}
//使用需要生成wsdl文件,创建客户端(自动生成相关类代码)
public static void clientCall() {
try {
HelloService_ServiceLocator locator = new HelloService_ServiceLocator();
//RMIConnection service = locator.get();
HelloService_PortType service = locator.getHelloServiceImplPort();
//如果服务端部署到外部tomcat时对URL进行了拦截,并设置了账号密码,这个账号密码是在http协议请求头的authorization属性里面
// (在tomcat-user.xml中配置用户名和密码,在web.xml中配置拦截URL),那就必须使用下面两行代码进行验证授权
// If authorization is required
((HelloServiceSoapBindingStub) service).setUsername("myUser");
((HelloServiceSoapBindingStub) service).setPassword("myPassword");
//服务端使用了拦截器,使用SOAPHeader进行身份验证
SOAPHeaderElement header = new SOAPHeaderElement(new QName("timamaes"));
SOAPElement element = header.addChildElement("username");
element.addTextNode("admin");
header.addChildElement("password").addTextNode("123456");
((HelloServiceSoapBindingStub) service).setHeader(header);
//System.out.println(header);
// invoke business method
String result = service.sayHello("java web service client");
System.out.println(result);
} catch (javax.xml.rpc.ServiceException ex) {
ex.printStackTrace();
} catch (java.rmi.RemoteException ex) {
ex.printStackTrace();
} catch (SOAPException e) {
e.printStackTrace();
}
}
//不需要生成客户端,也不需要wsdl文件,动态调用
public static void dynamicCall() {
String url = "http://localhost:8080/myspringbootdemo/soap/api?wsdl";
//String url = "http://localhost:8080/soap/api?wsdl";
Service service = new Service();
try {
Call call = (Call) service.createCall();
call.setTargetEndpointAddress(new URL(url));
// WSDL里面描述的接口名称(要调用的方法)
//QName中namespaceURI指的是自己WebService服务端设置的命名空间,即服务端@WebService注解的targetNamespace属性
//Qname中localPart指的是服务端发布的WebService中的方法名
call.setOperationName(new QName("http://services.myspringbootdemo.example.com",
"sayHello"));
//跨平台调用加上这个
//call.setUseSOAPAction(true);
//call.setSOAPActionURI("http://services.myspringbootdemo.example.com/sayHello");
// 接口方法的参数名, 参数类型,参数模式 IN(输入), OUT(输出) or INOUT(输入输出)
//这里特别注意,如果服务端没有使用注解@WebParam(name="ParaName")方式为服务端方法的参数设置名字,
//那么系统会自动使用arg0作为第一个参数名,arg1作为第二个参数名,依次类推。
//如果使用了该注解,那么参数名就跟注解定义的名字一样。
// 在wsdl文件中可以看到参数名到底是什么,在调用的时候一定要保证参数名称对应,否则会出现奇怪的错误
call.addParameter("arg0", XMLType.XSD_STRING, ParameterMode.IN);
// 设置被调用方法的返回值类型
call.setReturnType(XMLType.XSD_STRING);
//如果服务端部署到外部tomcat时对URL进行了拦截,并设置了账号密码,这个账号密码是在http协议头的authorization属性里面
// (在tomcat-user.xml中配置用户名和密码,在web.xml中配置拦截URL),那就必须使用下面两行代码进行验证授权
call.setUsername("myUser");
call.setPassword("myPassword");
//服务端使用了拦截器,使用SOAPHeader进行身份验证
SOAPHeaderElement header = new SOAPHeaderElement(new QName("timamaes"));
SOAPElement element = header.addChildElement("username");
element.addTextNode("admin");
header.addChildElement("password").addTextNode("123456");
call.addHeader(header);
// 设置方法中参数的值
Object result = call.invoke(new Object[]{"qinqi"});
System.out.println(result.toString());
} catch (ServiceException | MalformedURLException | RemoteException | SOAPException e) {
e.printStackTrace();
}
}
}
END!