这里的Java Web容器特指Tomcat,Tomcat依然是最流行的Java Web容器,你大爷还是你大爷。
本文并不涉及业务层面上的安全控制,只针对Tomcat自身所支持的相关安全控制功能与特性。
首先看一下Web容器的四个基本安全特性验证 Authentication
资源访问控制 Access control for resources
数据完整性 Data Integrity
数据机密性或私密性 Confidentiality or Data Privacy
每一项的细节这里不展开,并不难理解。
Java EE使用基于角色的访问控制——有用户、组、角色三个基本概念。
存储用户名和密码的地方叫Realm,有可能是文件,比如Tomcat的tomcat-users.xml,也可能是数据库,或者基于证书的机制。
Tomcat支持声明式验证和编程式验证,一个是基于XML配置,一个是通过代码实现。
声明式基本身份验证
在web.xml中定义
BASIC
同时可以定义角色
Admin User
admin
Manager
manager
针对路径做控制
Admin
/admin/*
admin
连HTTP方法也能配置
Manager
/manager/*
GET
POST
admin
manager
部署后,根据各服务器来配置用户和角色,比如Tomcat的就在conf/tomcat-users.xml里。
Tomcat的验证过程如下初次访问某个受保护的URI,Web容器会检查请求中是否包括Authorization头,如果没有,容器会返回401,以及WWW-Authenticate标头给浏览器,浏览器收到后会弹出对话框要求用户输入名称和密码。
如果用户输入用户名、密码正确后,浏览器会将用户名、密码以BASE64方式编码,然后放在Authorization标头中送出,容器进行验证,正确就将资源传回。
在关闭浏览器之前,对服务器的请求都包括Authorization头,服务器也每次都检查,所以登录有效期一直持续到关闭浏览器为止。
现在实验步骤如下
在Tomcat9目录下,修改conf/tomcat-users.xml文件,解注并新增admin的角色
在webapps目录下,新建test目录和/WEB-INF/web.xml文件
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaeehttp://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="true">
Servlet and JSP Examples.
Servlet and JSP Examples
UTF-8
Example Security Constraint
Protected Area - Allow methods
/admin/*
DELETE
GET
POST
PUT
admin
manager
BASIC
admin
manager
新建/admin目录和/test.html文件,
输入正确后,查看请求可以看到Authorization头
注意这种方式几乎跟裸奔没区别,只要能拦截到你的HTTP请求,就相当于查看到你的密码。上面Basic后的字符串用base64解密就是tomcat:123456。
如果需要自定义登录窗口,可以配置
FORM
/login.html
/error.html
登录表单的action和name属性是有要求的
除了FORM和BASIC,还可以设置为DIGEST或CLIENT_CERT。DIGEST提交时传递的是MD5加密后的摘要,CLIENT_CERT使用的是Public Key Certificate(PKC)加密,客户端要安装证书。
如果要启用HTTPS,则在下设置
CONFIDENTIAL
默认值是NONE,还可以设置为INTEGRAL,不过习惯设为CONFIDENTIAL,效果一样。
设置完后,认证的时候会自动跳转为HTTPS
编程式安全管理
支持编程能带来更灵活的控制。HttpServletRequest里跟安全有关的方法有authenticate( ): 是否登录,如果没有,返回false,并会转到登录界面
login( ): 登入
logout( ): 登出
getUserPrincipal( ): 取得代表用户的Principal对象
getRemoteUser( ): 获得登录用户的名称
isUserInRole(String role): 传入角色名称,检查登录用户是否属于该角色
@WebServlet("/secret")
public class User extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (request.authenticated(response)) { // 检查登录 // 执行登录后的用户能够做的事情 }
}
@WebServlet("/login")
public class Login extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("user");
String password = request.getParameter("passwd");
try {
request.login(username, password);
response.sendRedirect("user");
} catch( ServletException ex) {
response.sendRedirect("login.html");
}
}
和对标的注解是@ServletSecurity,比如
@WebServlet("/admin")
@ServletSecurity(@HttpConstraint(rolesAllowed = {"admin"}))
就表示/admin只允许admin角色访问。
再看一个复杂一点的
@WebServlet("/admin")
@ServletSecurity(
value=@HttpConstraint(rolesAllowed = { "admin", "manager" }),
httpMethodConstraints = {
@HttpMethodConstraint(value = "GET", rolesAllowed = {"admin", "manager"},
transportGuarantee = TransportGuarantee.CONFIDENTIAL),
@HttpMethodConstraint(value = "POST", rolesAllowed = {"admin", "manager"}),
)
其它Realm
前面的例子都是把用户名密码放在配置文件里,这样带来一定的隐患,在Tomcat里也支持其它的Realm,比如JDBCRealm
DataSourceRealm
JNDIRealm
UserDatabaseRealm
MemoryRealm
JAASRealm
CombinedRealm
LockOutRealm