发表于: 2018-03-06 22:40:26

1 1012


今日完成:

1.       JWT实现Token,如何进行验证

JWT中使用HAMC进行加密,

HMAC(Hash Message Authentication Code)哈希消息授权码,它在消息摘要算法(例如MD5,SHA系列算法)的基础上,使用密钥对消息摘要进行加密.它相当于一个马甲,内里可以使用MD5,SHA1,SHA256,SHA384,SHA512Message 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);

tokenheaderpreloadsignature三部分拆分,如果不能就抛出异常

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;
   
}
}

使用base64headerpreload进行解码

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);
}

读取并返回解码后的headerpreload

<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中的headerprolead使用数据库中的密钥和算法进行加密,与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中是通过调用HttpServletRequestgetSession方法(使用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 keyObjectvalue)方法读写客户状态信息。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的时候也不需要声明获取谁的SessionSession机制决定了当前客户只会获取到自己的Session,而不会获取到别人的Session。各客户的Session也彼此独立,互不可见。

 

psSession的使用比Cookie方便,但是过多的Session存储在服务器内存中,会对服务器造成压力。

 

Session保存在服务器端。为了获得更高的存取速度,服务器一般把Session放在内存里。每个用户都会有一个独立的Session。如果Session内容过于复杂,当大量客户访问服务器时可能会导致内存溢出。

Session在用户第一次访问服务器的时候自动创建。需要注意只有访问JSPServlet等程序时才会创建Session,只访问HTMLIMAGE等静态资源并不会创建Session。如果尚未生成Session,也可以使用request.getSession(true)强制生成Session

Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,并维护该Session。用户每访问服务器一次,无论是否读写Session,服务器都认为该用户的Session“活跃(active)”了一次。

 

Session的超时时间为maxInactiveInterval属性,可以通过对应的getMaxInactiveInterval()获取,通过setMaxInactiveInterval(longinterval)修改。

Session的超时时间也可以在web.xml中修改。另外,通过调用Sessioninvalidate()方法可以使Session失效。

 

void setAttribute(String attribute, Object value):设置Session属性。value参数可以为任何Java Object。通常为Java Beanvalue信息不宜过大

String getAttribute(String attribute):返回Session属性

Enumeration getAttributeNames():返回Session中存在的属性名

void removeAttribute(String attribute):移除Session属性

String getId():返回SessionID。该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失效

 

TomcatSession的默认超时时间为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的支持

服务器向客户端浏览器发送一个名为JSESSIONIDCookie,它的值为该Sessionid(也就是HttpSession.getId()的返回值)。Session依据该Cookie来识别是否为同一用户。

 

URL地址重写(一种是作为URL路径的附加信息,一种是作为查询字符串附加在URL后面)是对客户端不支持Cookie的解决方案。URL地址重写的原理是将该用户Sessionid信息重写到URL地址中。服务器能够解析重写后的URL获取Sessionid。这样即使客户端不支持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程序,由于大部分的手机浏览器都不支持CookieWAP程序都会采用URL地址重写来跟踪用户会话。

注意:TOMCAT判断客户端浏览器是否支持Cookie的依据是请求中是否含有Cookie。尽管客户端可能会支持Cookie,但是由于第一次请求时不会携带任何Cookie(因为并无任何Cookie可以携带),URL地址重写后的地址中仍然会带有jsessionid。当第二次访问时服务器已经在浏览器中写入Cookie了,因此URL地址重写后的地址中就不会带有jsessionid了。

 

Session中禁止使用Cookie

既然WAP上大部分的客户浏览器都不支持Cookie,索性禁止Session使用Cookie,统一使用URL地址重写会更好一些。Java Web规范支持通过配置的方式禁用Cookie。下面举例说一下怎样通过配置禁止使用Cookie

打开项目sessionWebWebRoot目录下的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便不会自动生成名JSESSIONIDCookieSession也不会以Cookie为识别标志,而仅仅以重写后的URL地址为识别标志了

注意:该配置只是禁止Session使用Cookie作为识别标志,并不能阻止其他的Cookie读写。也就是说服务器不会自动维护名为JSESSIONIDCookie了,但是程序中仍然可以读写其他的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.       JDBCODBC

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方法

查看SevletAPI可以看到,SingleThreadModel接口中没有定义任何方法和常量,在Java中,把没有定义任何方法和常量的接口称之为标记接口,经常看到的一个最典型的标记接口就是"Serializable",这个接口也是没有定义任何方法和常量的,标记接口在Java中有什么用呢?主要作用就是给某个对象打上一个标志,告诉JVM,这个对象可以做什么,比如实现了"Serializable"接口的类的对象就可以被序列化,还有一个"Cloneable"接口,这个也是一个标记接口,在默认情况下,Java中的对象是不允许被克隆的,就像现实生活中的人一样,不允许克隆,但是只要实现了"Cloneable"接口,那么对象就可以被克隆了。

Servlet实现了SingleThreadModel接口,只要在Servlet类的定义中增加实现SingleThreadModel接口的声明即可。 

对于实现了SingleThreadModel接口的ServletServlet引擎仍然支持对该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.       使用CookieSession进行登陆验证的过程以及相关代码



返回列表 返回列表
评论

    分享到