发表于: 2018-03-06 22:40:26
1 1012
今日完成:
1. JWT实现Token,如何进行验证
JWT中使用HAMC进行加密,
HMAC(Hash Message Authentication Code)哈希消息授权码,它在消息摘要算法(例如MD5,SHA系列算法)的基础上,使用密钥对消息摘要进行加密.它相当于一个马甲,内里可以使用MD5,SHA1,SHA256,SHA384,SHA512等Message Digest算法,在生成的消息摘要的基础上再多一道加密的工序.所以HMAC包括,HmacMD5,HmacSHA1,HmacSHA384,HmacSHA512等种类.正是因为HMAC只是一个马甲,它才有了很大的灵活性,底层的MessageDigest算法哪个好用哪个,说用哪个就用哪个,拆卸方便.
HAMC是非可逆加密算法,所以是无法解密的
1) 设置验证使用的加密算法和密钥
public static Algorithm HMAC256(String secret) throws IllegalArgumentException, UnsupportedEncodingException {
return new HMACAlgorithm("HS256", "HmacSHA256", secret);
}
BaseVerification(Algorithm algorithm) throws IllegalArgumentException {
if (algorithm == null) {
throw new IllegalArgumentException("The Algorithm cannot be null.");
} else {
this.algorithm = algorithm;
this.claims = new HashMap();
this.defaultLeeway = 0L;
}
}
2) 传入要验证的token进行解码
DecodedJWT jwt = JWT.decode(token);
将token按header、preload和signature三部分拆分,如果不能就抛出异常
static String[] splitToken(String token) throws JWTDecodeException {
String[] parts = token.split("\\.");
if (parts.length == 2 && token.endsWith(".")) {
parts = new String[]{parts[0], parts[1], ""};
}
if (parts.length != 3) {
throw new JWTDecodeException(String.format("The token was expected to have 3 parts, but got %s.", parts.length));
} else {
return parts;
}
}
使用base64对header和preload进行解码
try {
headerJson = StringUtils.newStringUtf8(Base64.decodeBase64(this.parts[0]));
payloadJson = StringUtils.newStringUtf8(Base64.decodeBase64(this.parts[1]));
} catch (NullPointerException var6) {
throw new JWTDecodeException("The UTF-8 Charset isn't initialized.", var6);
}
读取并返回解码后的header和preload
<T> T convertFromJSON(String json, Class<T> tClazz) throws JWTDecodeException {
if (json == null) {
throw this.exceptionForInvalidJson((String)null);
} else {
try {
return this.mapper.readValue(json, tClazz);
} catch (IOException var4) {
throw this.exceptionForInvalidJson(json);
}
}
}
3) 查看header定义的加密算法是否和数据库中使用的一致
private void verifyAlgorithm(DecodedJWT jwt, Algorithm expectedAlgorithm) throws AlgorithmMismatchException {
if (!expectedAlgorithm.getName().equals(jwt.getAlgorithm())) {
throw new AlgorithmMismatchException("The provided Algorithm doesn't match the one defined in the JWT's Header.");
}
}
4) 对token中的header和prolead使用数据库中的密钥和算法进行加密,与token中的signature是否一致
public void verify(DecodedJWT jwt) throws SignatureVerificationException {
byte[] contentBytes = String.format("%s.%s", jwt.getHeader(), jwt.getPayload()).getBytes(StandardCharsets.UTF_8);
byte[] signatureBytes = Base64.decodeBase64(jwt.getSignature());
try {
boolean valid = this.crypto.verifySignatureFor(this.getDescription(), this.secret, contentBytes, signatureBytes);
if (!valid) {
throw new SignatureVerificationException(this);
}
} catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException var5) {
throw new SignatureVerificationException(this, var5);
}
}
boolean verifySignatureFor(String algorithm, byte[] secretBytes, byte[] contentBytes, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException {
return MessageDigest.isEqual(this.createSignatureFor(algorithm, secretBytes, contentBytes), signatureBytes);
}
5) 验证服务器preload中的信息是否与token中的相符
this.verifyClaims(jwt, this.claims);
2. MD5编码
//按照MD5进行编码(准备,各就各位)
md.update(inputData);
//进行哈希计算(digest),并转换数据类型
bigInteger = new BigInteger(md.digest());
3. Cookie保存登陆信息
保存登录信息有多种方案。最直接的是把用户名与密码都保持到Cookie中,下次访问时检查Cookie中的用户名与密码,与数据库比较。这是一种比较危险的选择,一般不把密码等重要信息保存到Cookie中。
还有一种方案是把密码加密后保存到Cookie中,下次访问时解密并与数据库比较。这种方案略微安全一些。如果不希望保存密码,还可以把登录的时间戳保存到Cookie与数据库中,到时只验证用户名与登录时间戳就可以了。
单个cookie在客户端的限制是3K,就是说一个站点在客户端存放的COOKIE不能超过3K;
4. Session
Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些,相应的也增加了服务器的存储压力。
在Java中是通过调用HttpServletRequest的getSession方法(使用true作为参数)创建的。在创建了Session的同时,服务器会为该Session生成唯一的Session id,而这个Session id在随后的请求中会被用来重新获得已经创建的Session;在Session被创建之后,就可以调用Session相关的方法往Session中增加内容了,而这些内容只会保存在服务器中,发到客户端的只有Session id;当客户端再次发送请求的时候,会将这个Session id带上,服务器接受到请求之后就会依据Session id找到相应的Session,从而再次使用之。
Session对象是在客户端第一次请求服务器的时候创建的。Session也是一种key-value的属性对,通过getAttribute(Stringkey)和setAttribute(String key,Objectvalue)方法读写客户状态信息。Servlet里通过request.getSession()方法获取该客户的Session。
request还可以使用getSession(boolean create)来获取Session。区别是如果该客户的Session不存在,request.getSession()方法会返回null,而getSession(true)会先创建Session再将Session返回。
Servlet中必须使用request来编程式获取HttpSession对象,而JSP中内置了Session隐藏对象,可以直接使用。
程序中Session中直接保存了类对象,使用起来要比Cookie方便。当多个客户端执行程序时,服务器会保存多个客户端的Session。获取Session的时候也不需要声明获取谁的Session。Session机制决定了当前客户只会获取到自己的Session,而不会获取到别人的Session。各客户的Session也彼此独立,互不可见。
ps:Session的使用比Cookie方便,但是过多的Session存储在服务器内存中,会对服务器造成压力。
Session保存在服务器端。为了获得更高的存取速度,服务器一般把Session放在内存里。每个用户都会有一个独立的Session。如果Session内容过于复杂,当大量客户访问服务器时可能会导致内存溢出。
Session在用户第一次访问服务器的时候自动创建。需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session。如果尚未生成Session,也可以使用request.getSession(true)强制生成Session。
Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,并维护该Session。用户每访问服务器一次,无论是否读写Session,服务器都认为该用户的Session“活跃(active)”了一次。
Session的超时时间为maxInactiveInterval属性,可以通过对应的getMaxInactiveInterval()获取,通过setMaxInactiveInterval(longinterval)修改。
Session的超时时间也可以在web.xml中修改。另外,通过调用Session的invalidate()方法可以使Session失效。
void setAttribute(String attribute, Object value):设置Session属性。value参数可以为任何Java Object。通常为Java Bean。value信息不宜过大
String getAttribute(String attribute):返回Session属性
Enumeration getAttributeNames():返回Session中存在的属性名
void removeAttribute(String attribute):移除Session属性
String getId():返回Session的ID。该ID由服务器自动创建,不会重复
long getCreationTime():返回Session的创建日期。返回类型为long,常被转化为Date类型,例如:Date createTime = new Date(session.get CreationTime())
long getLastAccessedTime():返回Session的最后活跃时间。返回类型为
long int getMaxInactiveInterval():返回Session的超时时间。单位为秒。超过该时间没有访问,服务器认为该Session失效
void setMaxInactiveInterval(int second):设置Session的超时时间。单位为秒
boolean isNew():返回该Session是否是新创建的
void invalidate():使该Session失效
Tomcat中Session的默认超时时间为20分钟。
通过setMaxInactiveInterval(int seconds)修改超时时间。
修改web.xml改变Session的默认超时时间。
<session-config>
<session-timeout>60</session-timeout> <!-- 单位:分钟 -->
</session-config>
在server.xml中定义context时采用如下定义(单位为秒)
<Context path="/livsorder" docBase="/home/httpd/html/livsorder"
defaultSessionTimeOut="3600" isWARExpanded="true" isWARValidated="false" isInvokerEnabled="true"
isWorkDirPersistent="false"/>
Session需要Cookie的支持
服务器向客户端浏览器发送一个名为JSESSIONID的Cookie,它的值为该Session的id(也就是HttpSession.getId()的返回值)。Session依据该Cookie来识别是否为同一用户。
URL地址重写(一种是作为URL路径的附加信息,一种是作为查询字符串附加在URL后面)是对客户端不支持Cookie的解决方案。URL地址重写的原理是将该用户Session的id信息重写到URL地址中。服务器能够解析重写后的URL获取Session的id。这样即使客户端不支持Cookie,也可以使用Session来记录用户状态。HttpServletResponse类提供了encodeURL(String url)实现URL地址重写
把 session ID 加到一个连接可以使用一对方法来简化:response.encodeURL() 使 URL 包含 session ID,如果你需要使用重定向,可以使用 response.encodeRedirectURL () 来对 URL 进行编码。
encodeURL () 及 encodeRedirectedURL () 方法首先判断 cookies 是否被浏览器支持;如果支持,则参数 URL 被原样返回,session ID 将通过 cookies 来维持。
对于WAP程序,由于大部分的手机浏览器都不支持Cookie,WAP程序都会采用URL地址重写来跟踪用户会话。
注意:TOMCAT判断客户端浏览器是否支持Cookie的依据是请求中是否含有Cookie。尽管客户端可能会支持Cookie,但是由于第一次请求时不会携带任何Cookie(因为并无任何Cookie可以携带),URL地址重写后的地址中仍然会带有jsessionid。当第二次访问时服务器已经在浏览器中写入Cookie了,因此URL地址重写后的地址中就不会带有jsessionid了。
Session中禁止使用Cookie
既然WAP上大部分的客户浏览器都不支持Cookie,索性禁止Session使用Cookie,统一使用URL地址重写会更好一些。Java Web规范支持通过配置的方式禁用Cookie。下面举例说一下怎样通过配置禁止使用Cookie。
打开项目sessionWeb的WebRoot目录下的META-INF文件夹(跟WEB-INF文件夹同级,如果没有则创建),打开context.xml(如果没有则创建),编辑内容如下: /META-INF/context.xml:
<?xml version='1.0' encoding='UTF-8'?>
<Context path="/sessionWeb"cookies="false">
</Context>
或者修改Tomcat全局的conf/context.xml,修改内容如下: context.xml:
<!-- The contents of this file will be loaded for eachweb application -->
<Context cookies="false">
<!-- ... 中间代码略 -->
</Context>
部署后TOMCAT便不会自动生成名JSESSIONID的Cookie,Session也不会以Cookie为识别标志,而仅仅以重写后的URL地址为识别标志了
注意:该配置只是禁止Session使用Cookie作为识别标志,并不能阻止其他的Cookie读写。也就是说服务器不会自动维护名为JSESSIONID的Cookie了,但是程序中仍然可以读写其他的Cookie。
5. <%@page errorPage="error.jsp"%> 和 isErrorPage="true"
当jsp引擎在执行过程中发生了错误,那么jsp引擎会自动产生一个异常对象,如果这个Jsp页面指定了另外一个Jsp页面为错误处理程序,那么Jsp引擎将将这个异常对象放到request对象中,传到错误处理程序中去,因为page的编译指令isErrorPage设置为了True,那么Jsp引擎会自动生成一个exception对象,这个exception对象从request对象所包含的HTTP参数中获得.
Throwable exception=(Throwable)request.getAttribute(“javax.servlet.jspException”)
exception对象常用方法:
1.getMessage():返回错误信息
2.printStateTrace():该方法以标准形式输出一个错误和错误的堆栈
3.ToString();以字符串的形式返回一个对异常的描述
<%
out.println(exception.getMessage());
%>
<%
out.println(exception.toString());
%>
6. 转发与重定向,ModelAndView
转发在服务器端完成的;重定向是在客户端完成的
转发的速度快;重定向速度慢
转发的是同一次请求;重定向是两次不同请求
转发不会执行转发后的代码;重定向会执行重定向之后的代码
转发地址栏没有变化;重定向地址栏有变化
转发必须是在同一台服务器下完成;重定向可以在不同的服务器下完成
转发和重定向url时有斜线/和没有斜线的区别,有斜线是项目级别,无斜线是类级别下的
7. JDBC和ODBC
Java 可以使用 ODBC,但最好是在 JDBC 的帮助下以JDBC-ODBC桥的形式使用。ODBC 不适合直接在 Java 中使用,因为它使用 C 语言接口。从Java 调用本地 C代码在安全性、实现、坚固性和程序的自动移植性方面都有许多缺点。从 ODBC C API 到 Java API 的字面翻译是不可取的。
8. servlet并发访问
/**
17 * 加了synchronized后,并发访问i时就不存在线程安全问题了,
18 * 为什么加了synchronized后就没有线程安全问题了呢?
19 * 假如现在有一个线程访问Servlet对象,那么它就先拿到了Servlet对象的那把锁
20 * 等到它执行完之后才会把锁还给Servlet对象,由于是它先拿到了Servlet对象的那把锁,
21 * 所以当有别的线程来访问这个Servlet对象时,由于锁已经被之前的线程拿走了,后面的线
程只能排队等候了
22 *
23 */
24 synchronized (this) {//在java中,每一个对象都有一把锁,这里的this指的就是Servlet对象
25 i++;
26 try {
27 Thread.sleep(1000*4);
28 } catch (InterruptedException e) {
29 e.printStackTrace();
30 }
31 response.getWriter().write(i+"");
32 }
针对Servlet的线程安全问题,Sun公司是提供有解决方案的:让Servlet去实现一个SingleThreadModel接口,如果某个Servlet实现了SingleThreadModel接口,那么Servlet引擎将以单线程模式来调用其service方法
查看Sevlet的API可以看到,SingleThreadModel接口中没有定义任何方法和常量,在Java中,把没有定义任何方法和常量的接口称之为标记接口,经常看到的一个最典型的标记接口就是"Serializable",这个接口也是没有定义任何方法和常量的,标记接口在Java中有什么用呢?主要作用就是给某个对象打上一个标志,告诉JVM,这个对象可以做什么,比如实现了"Serializable"接口的类的对象就可以被序列化,还有一个"Cloneable"接口,这个也是一个标记接口,在默认情况下,Java中的对象是不允许被克隆的,就像现实生活中的人一样,不允许克隆,但是只要实现了"Cloneable"接口,那么对象就可以被克隆了。
让Servlet实现了SingleThreadModel接口,只要在Servlet类的定义中增加实现SingleThreadModel接口的声明即可。
对于实现了SingleThreadModel接口的Servlet,Servlet引擎仍然支持对该Servlet的多线程并发访问,其采用的方式是产生多个Servlet实例对象,并发的每个线程分别调用一个独立的Servlet实例对象。
实现SingleThreadModel接口并不能真正解决Servlet的线程安全问题,因为Servlet引擎会创建多个Servlet实例对象,而真正意义上解决多线程安全问题是指一个Servlet实例对象被多个线程同时调用的问题。事实上,在Servlet API 2.4中,已经将SingleThreadModel标记为Deprecated(过时的)。
明日计划:
1. 转发与重定向(带参数不带参数)
2. jdbc分页
3. 拦截器、过滤器、监听器,AOP
4. 任务5自媒体分享
5. 提交任务5
6. 开始任务六
遇到的问题:
1. 本地,已经将浏览器的cookie清空的前提下,启动一个不含登陆功能的项目,即不需要进行账号密码验证登陆的,为什么一打开项目,浏览器的cookie就会自动生成JSESSIONID,不应该是服务器上request.getSession(true)才生成session,同时自动产生一个session id???
收获:
1. 通过源码学习JWT验证
2. 使用Cookie和Session进行登陆验证的过程以及相关代码
评论