Java Web基础知识之安全:人生苦短,注意安全
关于web程序中的安全方面,想必大多数人都不甚了解,或者说感觉没有必要了解,身边开发网站的人主要就是注重后台的功能和前台的界面,不要说程序的安全问题,甚至后台数据库访问的问题可能都没有下大力气解决。但是这又是和我们密切相关的一个问题,每天看到网站哪个系统或者网站又出现安全问题都感觉离自己很遥远,其实这只是一个错觉,还是那句话——人生苦短,注意安全(某些人不要理解错了,说的就是你。。)。写这篇文章的时候,恰好想起来本屌丝考大学报志愿的时候,那时候北邮新开了一门专业叫信息安全,那个年代还不是很火,但是凭借本屌丝敏锐的洞察力(其实是情怀啦)一眼就看出来了该专业的前景,但是遗憾的是刚刚开办,还不招生。。。遗憾啊!!
保护我们的web程序可以通过声明和编程两种方式来完成,但是不管是哪种方式,都要满足web安全性的这4个方面:
- 验证:这是我们最熟悉的,每个一开始开发web程序的人都会做一个登录页面,这其实就是在验证web使用者的身份,这里的web使用者不一定是人,也可以是程序,比如某些爬虫程序想要爬取一些页面时,这就需要他们提供用户名和密码;
- 授权:关于这个应该也比较熟悉,但是由于我们不太关注,导致忽略了这一点,它主要关注被验证使用者的级别,是在上一步验证成功之后进行的,它的作用就是用来限制某个用户是否有权限进入web程序的某一个部分,直观点说,一个网站有普通用户,也有管理员,还有什么内容编辑等等,虽然他们都能登录成功,但是普通用户和页面编辑是不能进入网站的管理界面的,这就是他们没有得到网站拥有者的授权,这里的实现方式是通过建立角色来完成的,给予每个人特定的角色,然后规定一种角色能够访问web程序的哪些部分,最近Facebook奖励给发现Instagram漏洞的10岁儿童一万美金,就是出现了授权的漏洞。
- 加密:这就比较好理解了,因为数据自互联网上进行传输的时候是从一台计算机传到另一台计算机,等到了服务器时,可能已经经过了不止一台计算机,这就给别人拦截数据提供了极大地方便,因此我们需要对传输的数据进行加密,关于加密算法有很多,慕课网上有很多加密算法的讲解,可以去看一下;
- 完整:关于数据的完整性,简单来说虽然你加密了传输的数据,但是人家还是可以拦截,可能只是读不懂是什么意思,但是可以随便更改,到接收方就无法确认该数据是否还是从客户端发出的数据,这样就无法保证数据的完整性了,像数字电路中还有奇偶校验位来保证数据传输的正确,在web程序中可以通过建立一个安全通道来传输数据。
- 编程:其实大多数web程序使用的都是这种方式,我们不用看这篇文章都知道要怎么做,将用户输入的用户名和密码与存储在服务器上或者数据库中的进行验证,如果验证成功,再看该客户具体的角色。
- 声明:最大的好处是避免部分编程,因为验证和授权的部分是servlet容器完成的,而且在声明式安全中,浏览器可以在将用户名和密码发到服务器前对其进行加密,由于使用声明,所以所有的安全性约束都不用写到servlet类中,只要在部署描述符中进行声明即可,有很大的灵活性;但是声明这种方式也有缺点,支持数据加密的验证方法只能使用servlet容器(Tomcat)提供的默认登录框,不能定制,在这个看脸的社会无疑已经被淘汰出局,另外如果要使用定制的登录表单就不会对所传输的数据加密。
一、 声明式安全
<role rolename="manager-gui"/>
<role rolename="admin"/>
<role rolename="user"/>
<user password="tomcat" roles="manager-gui" username="tomcat"/>
<user password="lmy86263" roles="admin" username="lmy86263"/>
<user password="guest" roles="user" username="guest"/>
在配置tomcat的用户和角色时要注意,每次重启tomcat的时候,你在之前配置的用户和角色都会消失导致恢复到tomcat的默认状态,这是因为在eclipse中初次配置tomcat服务器时,eclipse会将tomcat的配置文件拷贝到自己的workspace下的server文件夹,每次启动读取的配置文件都是从这里读取的,而且还会用这里的配置覆盖tomcat目录下的配置文件,所以为了避免出现这种麻烦,我们将配置好的文件拷贝到workspace下的server文件夹一份即可,如下:
<security-constraint><web-resource-collection><web-resource-name>HttpServlet</web-resource-name><url-pattern>/myHttpServlet</url-pattern><http-method>GET</http-method><http-method>POST</http-method></web-resource-collection><auth-constraint><role-name>admin</role-name></auth-constraint>
</security-constraint><login-config></login-config>
关于上述几个元素,解释如下:
- security-constraint:用来指定一个资源集合和可以访问这些资源的一个或者多个角色。
- web-resource-collection:用来指定一组资源集合,这里面有几个元素比较重要,url-pattern就不说了,和之前使用servlet和Filter时一样,就是为了映射一个servlet资源,此处的映射只适用于直接访问该资源,如果是后台forward该资源或者使用JSP标签来访问时不受该安全机制约束的,这个元素可以有多个;http-method元素用来定义http方法,例如上述中使用了GET和POST方法说明安全性约束只适用于这两种方法,也就是说如果使用了PUT或者DELETE方法访问这些资源是不受该安全机制约束的,默认是所有方法都保护,这个元素也可以有多个;还有一个元素这里没有写出来是http-method-omission,这个正好和http-method相反,它是说明除了该属性中的方法之外的所有方法访问该资源时都会被限制,它不能和http-method一起使用;
- auth-constraint:用来指定可以访问该资源的角色名称,如果没有该元素则说明所有人都可以访问该元素,这样就没有意义;如果该元素存在但是是空的,说明没有人能够直接访问该资源(注意是直接);
1、 基本访问验证
<login-config><auth-method>BASIC</auth-method><realm-name>Admin only</realm-name>
</login-config>
使用这种Http验证方式,是将用户名和密码按照"用户名:密码"这种形式组合并且使用Base64算法进行编码传输到服务器,这种算法很弱,在网上随便找一个解码的网站都能知道你的用户名和密码。下面实现出现验证错误和没有授权时的截图:
这里可以看到虽然验证成功了,但是由于该用户对应的角色不在安全约束的范围之内,所以也是被禁止访问该资源的。
2、 摘要访问验证
<login-config><auth-method>DIGEST</auth-method><realm-name>Admin only</realm-name>
</login-config>
使用这种方式的请求和响应如下:
3、 表单访问验证
<login-config><auth-method>FORM</auth-method><realm-name>Admin only</realm-name><form-login-config><form-login-page>/login.jsp</form-login-page><form-error-page>/error.jsp</form-error-page></form-login-config>
</login-config>
关于错误处理页面比较简单这里就不说明了,但是登录页面中有几点要注意,登录页面如下:
<%@ page language="java" contentType="text/html; charset=GBK"pageEncoding="GBk"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Login</title>
</head>
<body><form action="j_security_check" method="post">用户名: <input type="text" name="j_username" >密码: <input type="password" name="j_password"><input type="submit"></form>
</body>
</html>
在表单中,注意action是j_security_check,用户名是j_username,密码是j_password,这三个字段都是由servlet容器来实现的,这里使用的是tomcat,在tomcat中处理这部分的类是org.apache.catalina.authenticator.FormAuthenticator,对应的代码在authenticate()方法中,如下:
boolean loginAction = (requestURI.startsWith(contextPath)) && (requestURI.endsWith("/j_security_check"));if (!loginAction){if ((request.getServletPath().length() == 0) && (request.getPathInfo() == null)){StringBuilder location = new StringBuilder(requestURI);location.append('/');if (request.getQueryString() != null){location.append('?');location.append(request.getQueryString());}response.sendRedirect(response.encodeRedirectURL(location.toString()));return false;}session = request.getSessionInternal(true);if (log.isDebugEnabled()) {log.debug("Save request in session '" + session.getIdInternal() + "'");}try{saveRequest(request, session);}catch (IOException ioe){log.debug("Request body too big to save during authentication");response.sendError(403, sm.getString("authenticator.requestBodyTooBig"));return false;}forwardToLoginPage(request, response, config);return false;}request.getResponse().sendAcknowledgement();Realm realm = this.context.getRealm();if (this.characterEncoding != null) {request.setCharacterEncoding(this.characterEncoding);}String username = request.getParameter("j_username");String password = request.getParameter("j_password");if (log.isDebugEnabled()) {log.debug("Authenticating username '" + username + "'");}principal = realm.authenticate(username, password);if (principal == null){forwardToErrorPage(request, response, config);return false;}if (log.isDebugEnabled()) {log.debug("Authentication of '" + username + "' was successful");}if (session == null) {session = request.getSessionInternal(false);}if (session == null){if (this.containerLog.isDebugEnabled()) {this.containerLog.debug("User took so long to log on the session expired");}if (this.landingPage == null){response.sendError(408, sm.getString("authenticator.sessionExpired"));}else{String uri = request.getContextPath() + this.landingPage;SavedRequest saved = new SavedRequest();saved.setMethod("GET");saved.setRequestURI(uri);saved.setDecodedRequestURI(uri);request.getSessionInternal(true).setNote("org.apache.catalina.authenticator.REQUEST", saved);response.sendRedirect(response.encodeRedirectURL(uri));}return false;}
二、 编程式安全
1、 使用注解
- @ServletSecurity:包括以下两个注解,对应于security-constraint元素;
- @HttpConstraint:主要用来添加允许访问该资源的角色,通过rolesAllowed配置;
- @HttpMethodContraint:主要用来添加被该安全机制所限制的Http方法,它里面也有一个rolesAllowed属性,和上面的注解中的属性要表达的意义是相同的,但是它只作用于;
@WebServlet(name="securityServlet", urlPatterns={"/securityServlet"})
@ServletSecurity(value=@HttpConstraint(rolesAllowed="user"), httpMethodConstraints={@HttpMethodConstraint(value="GET", rolesAllowed="admin")})
public class SecurityServlet extends HttpServlet {}
2、 使用API
- getAuthType():返回保护该Servlet的验证方法,对应的是web.xml中的<login-config>中的<auth-method>的值,如果没有验证方法则会返回null;
- getRemoteUser():返回发出该请求的用户的登录名,如果该用户没有通过验证则会返回null;
- isUserInRole():标明该经过验证的用户是否属于指定的角色,如果没有经过验证返回false;
- getUserPrincipal():返回包含被验证用户信息的java.security.Principal,如果未经过验证则返回null;
- authenticate():命令浏览器显示登录窗口用于对用户进行验证,验证方法使用表单方式时登录窗口为我们自定义的表单,否则使用servlet容器提供给我们的登录窗口;
- login():用于提供用户名和密码进行登录,登录成功不反返回任何值,登录失败则会抛出ServletException;
- logout():重置用户信息;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {if(req.authenticate(resp)){System.out.println("success");}else {System.out.println("fail");}System.out.println("AuthType: " + req.getAuthType());System.out.println("RemoteUser: " + req.getRemoteUser());System.out.println("isUserInRole: " + req.isUserInRole("admin"));System.out.println("UserPrincipal: " + req.getUserPrincipal());
}
这里使用表单验证方式,所以使用的表单是我们自定义的。
- 关于 Web 安全,99% 的网站都忽略了这些
- Tomcat keeps resetting my tomcat-users.xml file
- web.xml中和四种认证类型
- HTTP摘要认证
- 编程式安全策略配置
Java Web基础知识之安全:人生苦短,注意安全相关推荐
- java WEB 基础复习_开篇--Java Web基础知识
本篇主要是博客的开篇,是一些很基础的知识,我写博客的目的很简单.主要是供自己学习使用,督促自己学习,希望自己能坚持下来. 当前三大主流的Web动态网页技术是PHP(Personal Home Page ...
- java培训基础知识都学哪些
很多人都开始学习java技术,觉得java语言在未来的发展前景空间非常大,事实却是如此,那么针对于零基础的同学, 学习java技术需要学哪些呢?下面我们就来看看java培训基础知识都学哪些? java ...
- 你觉得什么才是 Java 的基础知识?
近日里,很多人邀请我回答各种j2ee开发的初级问题,我无一都强调java初学者要先扎实自己的基础知识,那什么才是java的基础知识?又怎么样才算掌握了java的基础知识呢?这个问题还真值得仔细思考. ...
- java ee基础知识_Java EE:基础知识
java ee基础知识 想要了解一些基本原则,即与Java EE相关的技术术语. 对于许多人来说,Java EE / J2EE仍然最多意味着Servlet,JSP或Struts. 没有冒犯或双关语! ...
- hashcode是什么意思_什么才是 Java 的基础知识?
作者:晓风轻 链接:zhuanlan.zhihu.com/p/28615617 近日里,很多人邀请我回答各种j2ee开发的初级问题,我无一都强调java初学者要先扎实自己的基础知识,那什么才是java ...
- Java Web基础性知识
Java Web基础性知识 下面是对Java web 最基本知识的介绍 Java web 包含的一些语言用法 下面是一些基本的标签用法以及实现功能 下面是对Java web 最基本知识的介绍 我们都知 ...
- Java面试基础知识III
Java面试基础知识: 1.C++或Java中的异常处理机制的简单原理和应用. 当JAVA 程序违反了JAVA的语义规则时,JAVA虚拟机就会将发生的错误表示为一个异常.违反语义规则包括2种情况.一种 ...
- 什么才是java的基础知识?
作者:晓风 出处:https://xwjie.github.io 关于资源视频下载的说明 常用设计模式完整系列篇 [强化编程功底]算法文摘 近日里,很多人邀请我回答各种j2ee开发的初级问题,我无一都 ...
- Java进阶基础知识
Java进阶基础知识 1.Java 基础 Java类设计的原则就是内聚性,一致性和封装性是Java设计的基本原则 1.1 Java基础理论 Java基础理论知识 1.2继承的优缺点 1.2.1优点 : ...
最新文章
- C# 获取电脑的网络连接状态
- chrome 控制台js调试与断点调试
- Windows内核的表学习总结
- Python--切片学习记录
- 我用的 cordova 插件
- 关于JavaScript中的几种匿名行数的写法
- pdffactory字体打印不对_标准论文格式字体要求
- [Android]发布Sqlite数据库
- 如何上传文件夹到GitHub上(配图详解)
- springContext
- oracle erp crm系统,企业集成ERP和CRM系统的模式体验
- 华为简单静态路由配置
- 【验证码识别】OpenCV挑战极验滑动拼图验证码
- java 某字段重复的数据库,excel表格两个字段去重复的数据库【用JAVA程序向SQL数据库导入Excel表,判断出SQL表中已存在的重复数据,并跳过重复的继续导入其他记录.】...
- 队爷的讲学计划(tarjan +拓扑排序)
- Unity 钓鱼玩法的初步实现
- 手把手教你R语言做k均值聚类分析
- 【第三弹】经典移植至IOS端、经典合集
- 潮流分析matlab课程设计小结,电力系统暂态分析课程设计--基于MATLAB的电力系统复杂潮流分析...
- 计算机控制技术的前景,计算机控制技术专业就业前景如何?