基于token的登陆验证机制 - 程序员自由之路 - 博客园

mikel阅读(764)

来源: 基于token的登陆验证机制 – 程序员自由之路 – 博客园

session简介

做过Web开发的程序员应该对Session都比较熟悉,Session是一块保存在服务器端的内存空间,一般用于保存用户的会话信息。

用户通过用户名和密码登陆成功之后,服务器端程序会在服务器端开辟一块Session内存空间并将用户的信息存入这块空间,同时服务器会
在cookie中写入一个Session_id的值,这个值用于标识这个内存空间。

下次用户再来访问的话会带着这个cookie中的session_id,服务器拿着这个id去寻找对应的session,如果session中已经有了这个用户的
登陆信息,则说明用户已经登陆过了。

使用Session保持会话信息使用起来非常简单,技术也非常成熟。但是也存在下面的几个问题:

  • 服务器压力大:通常Session是存储在内存中的,每个用户通过认证之后都会将session数据保存在服务器的内存中,而当用户量增大时,服务器的压力增大。
  • Session共享:现在很多应用都是分布式集群,需要我们做额外的操作进行Session共享;
  • CSRF跨站伪造请求攻击:Session机制是基于浏览器端的cookie的,cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

基于token的认证

基于token的认证机制将认证信息返回给客户端并存储。下次访问其他页面,需要从客户端传递认证信息回服务端。简单的流程如下:

  • 客户端使用用户名跟密码请求登录;
  • 服务端收到请求,去验证用户名与密码;
  • 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端;
  • 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里;
  • 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token;
  • 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据;

基于token的验证机制,有以下的优点:

  • 支持跨域访问,将token置于请求头中,而cookie是不支持跨域访问的;
  • 无状态化,服务端无需存储token,只需要验证token信息是否正确即可,而session需要在服务端存储,一般是通过cookie中的sessionID在服务端查找对应的session;
  • 无需绑定到一个特殊的身份验证方案(传统的用户名密码登陆),只需要生成的token是符合我们预期设定的即可;
  • 更适用于移动端(Android,iOS,小程序等等),像这种原生平台不支持cookie,比如说微信小程序,每一次请求都是一次会话,当然我们可以每次去手动为他添加cookie,详情请查看博主另一篇博客;
  • 避免CSRF跨站伪造攻击,还是因为不依赖cookie;

缺点的话一个就是相比较于传统的session登陆机制实现起来略微复杂一点,另外一个比较大的缺点是由于服务器不保存 token,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 token 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。

退出登陆的话,只要前端清除token信息即可。

基于JWT的token认证实现

JWT(JSON Web Token)就是基于token认证的代表,这边就用JWT为列来介绍基于token的认证机制。

需要引入JWT的依赖

<dependency>
  <groupId>com.auth0</groupId>
  <artifactId>java-jwt</artifactId>
  <version>3.8.2</version>
</dependency>

生成token和验证token的工具类如下:

public class JWTTokenUtil {
    //设置过期时间
    private static final long EXPIRE_DATE=30*60*100000;
    //token秘钥
    private static final String TOKEN_SECRET = "ZCfasfhuaUUHufguGuwu2020BQWE";

    public static String token (String username,String password){

        String token = "";
        try {
            //过期时间
            Date date = new Date(System.currentTimeMillis()+EXPIRE_DATE);
            //秘钥及加密算法
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            //设置头部信息
            Map<String,Object> header = new HashMap<>();
            header.put("typ","JWT");
            header.put("alg","HS256");
            //携带username,password信息,生成签名
            token = JWT.create()
                    .withHeader(header)
                    .withClaim("username",username)
                    .withClaim("password",password).withExpiresAt(date)
                    .sign(algorithm);
        }catch (Exception e){
            e.printStackTrace();
            return  null;
        }
        return token;
    }

    public static boolean verify(String token){
        /**
         * @desc   验证token,通过返回true
         * @params [token]需要校验的串
         **/
        try {
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return  false;
        }
    }
    public static void main(String[] args) {
        String username ="name1";
        String password = "pw1";
        //注意,一般不会把密码等私密信息放在payload中,这边只是举个列子
        String token = token(username,password);
        System.out.println(token);
        boolean b = verify(token);
        System.out.println(b);
    }
}

执行结果如下:

Connected to the target VM, address: '127.0.0.1:11838', transport: 'socket'
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXNzd29yZCI6IjEyMyIsImV4cCI6MTU5NzM5Nzc0OCwidXNlcm5hbWUiOiJ6aGFuZ3NhbiJ9.LI5S_nX-YcqtExI9UtKiP8FPqpQW_ccaws2coLzyOS0
true

关于DecodedJWT这个类,大家可以重点看下,里面包含了解码后的用户信息。

JWT的使用说明

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。

此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。

Authorization: Bearer <token>

另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。

JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。(或者是对JWT在前后端之间进行加密之后在传输)

关于JWT的一个问题

上面生成JWT token的过程关键点就是密钥,假如这个密钥泄露了,那是不是就可以伪造token了。

还有就是生产环境的密钥值,开发的程序员大概率是知道的,怎么防止程序要监守自盗,伪造token值呢?希望有经验的大佬指教下。

//token秘钥
private static final String TOKEN_SECRET = "ZCfasfhuaUUHufguGuwu2020BQWE";

关于上面的问题,@仙湖码农 给出了一个简单易懂的方案~

jwt 来生成token,还有一个玩法,用户登录时,生成token的 SecretKey 是一个随机数,也就是说每个用户,每次登录时jwt SecretKey 是随机数,并保存到缓存,key是登录账户,(当然了,分布式缓存的话,就用Redis,SQLServer缓存等等),总之,客户端访问接口是,header 要带登录账户,和token,服务端拿到登录账号,到缓存去捞相应的SecretKey ,然后再进行token校验。可以防伪造token了(这个方案在一定程度上能防止伪造,但是不能防止token泄露被劫持)。

获取refresh token的方法

参考

基于jwt的token验证 - 奕锋博客 - 博客园

mikel阅读(986)

来源: 基于jwt的token验证 – 奕锋博客 – 博客园

一、什么是JWT

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).
该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

二、JWT的组成

1、JWT生成编码后的样子

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q

2、JWT由三部分构成

第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).

 header

jwt的头部承载两部分信息:

  • 声明类型,这里是jwt
  • 声明加密的算法 通常直接使用 HMAC SHA256

完整的头部就像下面这样的JSON:

{
  'typ': 'JWT',
  'alg': 'HS256'
}

然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

playload

载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分

  • 标准中注册的声明
  • 公共的声明
  • 私有的声明

标准中注册的声明 (建议但不强制使用) :

  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的.
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

公共的声明 :
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

 私有的声明 :

私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

定义一个payload:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

然后将其进行base64加密,得到Jwt的第二部分

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

signature

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

  • header (base64后的)
  • payload (base64后的)
  • secret

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串(头部在前),然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q

密钥secret是保存在服务端的,服务端会根据这个密钥进行生成token和验证,所以需要保护好。

3、签名的目的

最后一步签名的过程,实际上是对头部以及载荷内容进行签名。一般而言,加密算法对于不同的输入产生的输出总是不一样的。对于两个不同的输入,产生同样的输出的概率极其地小(有可能比我成世界首富的概率还小)。所以,我们就把“不一样的输入产生不一样的输出”当做必然事件来看待吧。

所以,如果有人对头部以及载荷的内容解码之后进行修改,再进行编码的话,那么新的头部和载荷的签名和之前的签名就将是不一样的。而且,如果不知道服务器加密的时候用的密钥的话,得出来的签名也一定会是不一样的。

服务器应用在接受到JWT后,会首先对头部和载荷的内容用同一算法再次签名。那么服务器应用是怎么知道我们用的是哪一种算法呢?别忘了,我们在JWT的头部中已经用alg字段指明了我们的加密算法了。

如果服务器应用对头部和载荷再次以同样方法签名之后发现,自己计算出来的签名和接受到的签名不一样,那么就说明这个Token的内容被别人动过的,我们应该拒绝这个Token,返回一个HTTP 401 Unauthorized响应。

注意:在JWT中,不应该在载荷里面加入任何敏感的数据,比如用户的密码。

4、如何应用

一般是在请求头里加入Authorization,并加上Bearer标注:

fetch('api/user/1', {
  headers: {
    'Authorization': 'Bearer ' + token
  }
})

服务端会验证token,如果验证通过就会返回相应的资源。

5、安全相关

  • 不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。
  • 保护好secret私钥,该私钥非常重要。
  • 如果可以,请使用https协议

6、对Token认证的五点认识

  • 一个Token就是一些信息的集合;
  • 在Token中包含足够多的信息,以便在后续请求中减少查询数据库的几率;
  • 服务端需要对cookie和HTTP Authrorization Header进行Token信息的检查;
  • 基于上一点,你可以用一套token认证代码来面对浏览器类客户端和非浏览器类客户端;
  • 因为token是被签名的,所以我们可以认为一个可以解码认证通过的token是由我们系统发放的,其中带的信息是合法有效的;

三、传统的session认证

我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。

但是这种基于session的认证使应用本身很难得到扩展,随着不同客户端用户的增加,独立的服务器已无法承载更多的用户,而这时候基于session认证应用的问题就会暴露出来。

基于session认证所显露的问题

Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。

扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。

CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

基于token的鉴权机制

基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。

流程上是这样的:

  • 用户使用用户名密码来请求服务器
  • 服务器进行验证用户的信息
  • 服务器通过验证发送给用户一个token
  • 客户端存储token,并在每次请求时附送上这个token值
  • 服务端验证token值,并返回数据

这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了 Access-Control-Allow-Origin:*。

四、token的优点

  • 支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输。
  • 无状态(也称:服务端可扩展行):Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息。
  • 更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:JavaScript,HTML,图片等),而你的服务端只要提供API即可。
  • 去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可。
  • 更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。
  • CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。
  • 性能: 一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算 的Token验证和解析要费时得多。
  • 不需要为登录页面做特殊处理: 如果你使用Protractor 做功能测试的时候,不再需要为登录页面做特殊处理。
  • 基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft)。
  • 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
  • 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
  • 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
  • 它不需要在服务端保存会话信息, 所以它易于应用的扩展。

五、JWT的JAVA实现

Java中对JWT的支持可以考虑使用JJWT开源库;JJWT实现了JWT, JWS, JWE 和 JWA RFC规范;下面将简单举例说明其使用:

1、生成Token码

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import io.jsonwebtoken.*;
import java.util.Date;    
 
//Sample method to construct a JWT
 
private String createJWT(String id, String issuer, String subject, long ttlMillis) {
 
//The JWT signature algorithm we will be using to sign the token
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
 
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
 
//We will sign our JWT with our ApiKey secret
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(apiKey.getSecret());
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
 
  //Let's set the JWT Claims
JwtBuilder builder = Jwts.builder().setId(id)
                                .setIssuedAt(now)
                                .setSubject(subject)
                                .setIssuer(issuer)
                                .signWith(signatureAlgorithm, signingKey);
 
//if it has been specified, let's add the expiration
if (ttlMillis >= 0) {
    long expMillis = nowMillis + ttlMillis;
    Date exp = new Date(expMillis);
    builder.setExpiration(exp);
}
 
//Builds the JWT and serializes it to a compact, URL-safe string
return builder.compact();
}

2、解码和验证Token码

import javax.xml.bind.DatatypeConverter;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.Claims;
 
//Sample method to validate and read the JWT
private void parseJWT(String jwt) {
//This line will throw an exception if it is not a signed JWS (as expected)
Claims claims = Jwts.parser()        
   .setSigningKey(DatatypeConverter.parseBase64Binary(apiKey.getSecret()))
   .parseClaimsJws(jwt).getBody();
System.out.println("ID: " + claims.getId());
System.out.println("Subject: " + claims.getSubject());
System.out.println("Issuer: " + claims.getIssuer());
System.out.println("Expiration: " + claims.getExpiration());
}

 

文章整理自:

https://blog.csdn.net/buyaoshuohua1/article/details/73739419

https://www.cnblogs.com/xiekeli/p/5607107.html#top

https://blog.csdn.net/SoftwareOscar/article/details/78538346

https://blog.csdn.net/Jack__Frost/article/details/64964208

http://blog.leapoahead.com/2015/09/06/understanding-jwt/

什么是token及怎样生成token - 路飞撸代码 - 博客园

mikel阅读(1500)

来源: 什么是token及怎样生成token – 路飞撸代码 – 博客园

什么是token

  Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
  基于 Token 的身份验证
  1. 使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。流程是这样的:
  2. 客户端使用用户名跟密码请求登录
  3. 服务端收到请求,去验证用户名与密码
  4. 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
  5. 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
  6. 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
  7. 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
  8. APP登录的时候发送加密的用户名和密码到服务器,服务器验证用户名和密码,如果成功,以某种方式比如随机生成32位的字符串作为token,存储到服务器中,并返回token到APP,以后APP请求时,
  9. 凡是需要验证的地方都要带上该token,然后服务器端验证token,成功返回所需要的结果,失败返回错误信息,让他重新登录。其中服务器上token设置一个有效期,每次APP请求的时候都验证token和有效期。

token的优势

  1.无状态、可扩展

在客户端存储的Tokens是无状态的,并且能够被扩展。基于这种无状态和不存储Session信息,负载负载均衡器能够将用户信息从一个服务传到其他服务器上。如果我们将已验证的用户的信息保存在Session中,则每次请求都需要用户向已验证的服务器发送验证信息(称为Session亲和性)。用户量大时,可能会造成  一些拥堵。但是不要着急。使用tokens之后这些问题都迎刃而解,因为tokens自己hold住了用户的验证信息。

2.安全性

请求中发送token而不再是发送cookie能够防止CSRF(跨站请求伪造)。即使在客户端使用cookie存储token,cookie也仅仅是一个存储机制而不是用于认证。不将信息存储在Session中,让我们少了对session操作。token是有时效的,一段时间之后用户需要重新验证。我们也不一定需要等到token自动失效,token有撤回的操作,通过token revocataion可以使一个特定的token或是一组有相同认证的token无效。

3.可扩展性

Tokens能够创建与其它程序共享权限的程序。例如,能将一个随便的社交帐号和自己的大号(Fackbook或是Twitter)联系起来。当通过服务登录Twitter(我们将这个过程Buffer)时,我们可以将这些Buffer附到Twitter的数据流上(we are allowing Buffer to post to our Twitter stream)。使用tokens时,可以提供可选的权限给第三方应用程序。当用户想让另一个应用程序访问它们的数据,我们可以通过建立自己的API,得出特殊权限的tokens。

4.多平台跨域

我们提前先来谈论一下CORS(跨域资源共享),对应用程序和服务进行扩展的时候,需要介入各种各种的设备和应用程序。Having our API just serve data, we can also make the design choice to serve assets from a CDN. This eliminates the issues that CORS brings up after we set a quick header configuration for our application.只要用户有一个通过了验证的token,数据和资源就能够在任何域上被请求到。Access-Control-Allow-Origin: *

  5.基于标准

  创建token的时候,你可以设定一些选项。我们在后续的文章中会进行更加详尽的描述,但是标准的用法会在JSON Web Tokens体现。最近的程序和文档是供给JSON Web Tokens的。它支持众多的语言。这意味在未来的使用中你可以真正的转换你的认证机制。

token原理

1.将荷载payload,以及Header信息进行Base64加密,形成密文payload密文,header密文。

2.将形成的密文用句号链接起来,用服务端秘钥进行HS256加密,生成签名.

3.将前面的两个密文后面用句号链接签名形成最终的token返回给服务端

注:

(1)用户请求时携带此token(分为三部分,header密文,payload密文,签名)到服务端,服务端解析第一部分(header密文),用Base64解密,可以知道用了什么算法进行签名,此处解析发现是HS256。

(2)服务端使用原来的秘钥与密文(header密文+”.”+payload密文)同样进行HS256运算,然后用生成的签名与token携带的签名进行对比,若一致说明token合法,不一致说明原文被修改。

(3)判断是否过期,客户端通过用Base64解密第二部分(payload密文),可以知道荷载中授权时间,以及有效期。通过这个与当前时间对比发现token是否过期。

token实现思路

1.用户登录校验,校验成功后就返回Token给客户端。

2.客户端收到数据后保存在客户端

3.客户端每次访问API是携带Token到服务器端。

4.服务器端采用filter过滤器校验。校验成功则返回请求数据,校验失败则返回错误码

token代码生成工具类demo

package com.frank.common.utils;

import com.alibaba.fastjson.JSON;
import com.frank.common.entity.TokenHeader;
import com.frank.common.entity.TokenPlayload;
import com.frank.common.entity.User;

import java.rmi.server.UID;
import java.util.UUID;

/**
* Description:Token生成工具
* 第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).
* Auth: Frank
* Date: 2017-11-02
* Time: 下午 5:05
*/
public class TokenUtil {

public static final String TOKEN_AES_KEY = “xiangli8Token”;
public static final String REFREH_TOKEN_AES_KEY = “xiangli8RefreshToken”;
public static final String JWT_TYP = “JWT”;
public static final String JWT_ALG = “AES”;
public static final String JWT_EXP = “30”;
public static final String JWT_ISS = “xiangli8”;

/**
* 获得token
* @param data 自定义数据
* @param <T> 自定义数据
* @return
* @throws Exception
*/
public static <T> String getToken(T data) throws Exception {
TokenPlayload<T> userTokenPlayload = new TokenPlayload<>();
userTokenPlayload.setExpData(data);
String jwt = createJWT(userTokenPlayload);
return jwt;
}

/**
* 生成jwt的header部分内容
* @return
* @throws Exception
*/
private static String tokenHeaderBase64() throws Exception {
TokenHeader tokenHeader = new TokenHeader();
tokenHeader.setTyp(JWT_TYP);
tokenHeader.setAlg(JWT_ALG);

String headerJson = JSON.toJSONString(tokenHeader);

String headerBase64 = Base64Util.encryptBASE64(headerJson.getBytes());

return headerBase64;
}

/**
* 生成jwt的payload部分内容
* @param tokenPlayload
* @param <T>自定义的数据块
* @return
* @throws Exception
*/
private static <T> String tokenPayloadBase64(TokenPlayload<T> tokenPlayload) throws Exception {
tokenPlayload.setIss(JWT_ISS);
tokenPlayload.setExp(JWT_EXP);

tokenPlayload.setIat(String.valueOf(System.currentTimeMillis()));

String headerJson =JSON.toJSONString(tokenPlayload);

String headerBase64 = Base64Util.encryptBASE64(headerJson.getBytes());

return headerBase64;
}

/**
* 生成JWT
* @return
*/
public static <T> String createJWT(TokenPlayload<T> tokenPlayload) throws Exception {
StringBuilder jwtSb = new StringBuilder();
StringBuilder headerPlayloadSb = new StringBuilder();

String tokenHeaderBase64 = tokenHeaderBase64();
String tokenPayloadBase64 = tokenPayloadBase64(tokenPlayload);

jwtSb.append(tokenHeaderBase64);
jwtSb.append(“.”);
jwtSb.append(tokenPayloadBase64);
jwtSb.append(“.”);

headerPlayloadSb.append(tokenHeaderBase64);
headerPlayloadSb.append(tokenPayloadBase64);

String headerPlayloadSalt = SaltUtil.addSalt(headerPlayloadSb.toString());

String key = AesUtil.initKey(TOKEN_AES_KEY+tokenPlayload.getIat());

String signature = Base64Util.encryptBASE64(AesUtil.encrypt(headerPlayloadSalt.getBytes(),key));

jwtSb.append(signature);

return Base64Util.encryptBASE64(jwtSb.toString().getBytes());
}

/**
* 校验token是否是服务器生成的,以防token被修改
* @param jwtBase64
* @return
* @throws Exception
*/
public static <T> boolean verifyJWT(String jwtBase64) throws Exception {
String jwt = new String (Base64Util.decryptBASE64(jwtBase64));

if(!jwt.contains(“.”)){
return false;
}

String[] jwts = jwt.split(“\\.”);
if(jwts.length<3){
return false;
}

TokenPlayload tTokenPlayload = JSON.parSEObject(new String(Base64Util.decryptBASE64(jwts[1])),TokenPlayload.class);
String key = AesUtil.initKey(TOKEN_AES_KEY+tTokenPlayload.getIat());

//解析出header跟playload
StringBuilder headerPlayloadSb = new StringBuilder();
headerPlayloadSb.append(jwts[0]);
headerPlayloadSb.append(jwts[1]);

//解析signature
String headerPlayloadSalt = new String (AesUtil.decrypt(Base64Util.decryptBASE64(jwts[2]),key));

return SaltUtil.verifyPwd(headerPlayloadSb.toString(),headerPlayloadSalt);
}

 

public static void main(String[] args) throws Exception {
String jwt = getToken(new User(1L,”你是逗逼”));
System.out.println(“jwt:”+jwt);
System.out.println(“verifyJWT:”+verifyJWT(jwt));
}
}

使用说明

1,根据上面生成一个由base64编码的token,该token由Header,Payload,Signature组成。

2,token作为用户请求的标识,客户端保存这token的全部信息。服务端只需要保存token的Signature部分。

3,服务端把token的Signature存于redis和服务器的数据库中。

4,客户端请求的数据附带token,服务端拿到token,首先校验token,以防token伪造。校验规则如下:

4.1,拆分出token的Header,Payload,Signature。

4.2,校验Signature,通过token的header和payload生成Signature,看看生成的Signature是否和客户端附带上来的Signature一致。如果一致继续请求操作,不一致则打回操作

4.3,查看Signature是否存在服务器的redis和数据库中。如果不存在则打回请求操作

win10上 安装Sqlserver2008R2数据库提示.NET Framework3.5不存在,解决之后仍然提示兼容问题安装不成功_一米阳光-CSDN博客

mikel阅读(4349)

来源: win10上 安装Sqlserver2008R2数据库提示.NET Framework3.5不存在,解决之后仍然提示兼容问题安装不成功_一米阳光-CSDN博客

最近项目开发过程中,需要在win10系统上安装SQLServer2008R2的时候,刚开始提示需要安装.NET Framework3.5版本,通过360软件管家安装之后,仍然不行,安装SQLServer的时候提示截图如下:

试了网上好多方法,仍然不行,然后参考了其他网站的一个解决方法,采用cab格式的.NET Framework 3.5离线安装包格式进行安装。

安装方法:

先把下载的名为NetFx3.cab的离线安装包放到Win10系统盘C:\Windows文件夹里。

然后以管理员身份运行命令提示符(按 win + x ,如下图 ),输入并回车运行以下命令:

 

dism /online /Enable-Feature /FeatureName:NetFx3 /Source:”%windir%” /LimitAccess

等待部署进度100%即可。
cab格式的.NET Framework3.5下载地址:http://download.csdn.net/detail/zhangzuomian/9789495

参考网站地址:https://www.windows10.pro/net-framework-3-5-netfx3-cab-download/

这样就把.NET Framework3.5安装成功了。

安装.NET Framework3.5之后,继续安装SQLServer,刚开始以兼容模式和管理员模式进行SQLserver2008R2数据库文件的安装,但是最后总是提示失败。网上看了很多解决方法,有的朋友说要更换系统,有的说需要下载sp3,最后仍然没有解决。

怎么解决呢?最后发现win10下安装SQLserver2008R2的时候,不要使用兼容模式安装,然后也不用右键选择管理员模式安装,直接双击安装就可以。

我也不知道为什么会这样,反复试了几次,用兼容模式安装会提示失败,不用兼容模式就没有问题。
————————————————
版权声明:本文为CSDN博主「张懿勉」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ZhangZuoMian/article/details/64905543

安装Sql server 2008时提示没有安装.net 3.5的解决办法_doyouknowm的专栏-CSDN博客

mikel阅读(1793)

来源: 安装Sql server 2008时提示没有安装.net 3.5的解决办法_doyouknowm的专栏-CSDN博客

将dotNetFx35setup.exe拷贝至对应目录下(由操作系统而定),如下:

x86\redist\DotNetFrameworks\dotNetFx35setup.exe

x64\redist\DotNetFrameworks\dotNetFx35setup.exe

如果还有其他问题,比如WindowsInstaller 4.5的安装失败或者语言包的问题,同样的处理方式。
————————————————
版权声明:本文为CSDN博主「doyouknowm」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/doyouknowm/article/details/61617892

Notepad++ 搜索功能学习总结_bcbobo21cn的专栏-CSDN博客

mikel阅读(1669)

来源: Notepad++ 搜索功能学习总结_bcbobo21cn的专栏-CSDN博客

一 实例

先看一下查找菜单;

查找对话框;

 

标记功能;选中 标记所在行;

 

标记后的情况如下图;

 

 

文件查找功能;下图是在 C:\Windows\Microsoft.NET\Framework\v4.0.30319目录下的所有文件(含子目录)中查找字符串”xml”;

 

最后找到的结果如下所示;

 

增量查找;我操作的时候 增量查找 功能应该是默认开启的; 在下部输入框输入查找内容,每输入一个字符就会自动显示文档中匹配的部分,不用全部输入完再打回车才显示,这就是增量查找的含义;

 

列编辑;按住Alt后,即是列编辑模式;按住Alt,再用鼠标选取,可以不用整行的选取;如下图;

 

二 Notepad++查找功能
Notepad++中的高级查找

无论对于哪一种编辑器,基本上都支持正则表达式查找,在Notepad++中也不例外,今天我们一块看下如何使用正则表达式进行查找。

工具/原料
Notepad++
方法/步骤
1
准备以下字符串用来演示
abcdeab
cdeabcde
abcd
eabcde

2
基于扩展的查找
基于扩展的查找不能算是真正的正则表达式搜索,因此这种查找方式仅是提供了支持转义字符。主要常用的转义字符包含了:\r\n\t等。
我们分别使用下面的搜索条件进行查找:
a.*\r\n
abcd\r\n
会发现使用a.*\r\n是无法查找到内容。这也说明了在我们选择“扩展”项时不支持正则条件。

3
使用正则表达式
对于 正则表达式的使用方法我们就不介绍了,这里我们搜索abcd字符串。使用下面的正则进行匹配,为了便于显示,我们使用“标记”功能:
a.+?d
上面的正则表达式的意思是搜索字符串中以a开始,后面有一个或多个字符(.代表除了\n之外的其他字符,+代表匹配一次或多次,?代表非贪婪,即从当前字符开始向后一个一个字符的匹配),最后以d字符结尾。
我们会发现匹配到了4个,可能就会有人问了,那第一行结尾的ab与第二行的cd也应该匹配啊,其实原因就是在于对于正则表达式来说,回车换行代表了一个新的段落开始,如果想匹配那么就涉及到了匹配模式,下面会说明如何进行匹配。

4
匹配新行
为了解决第3步中我们的问题,在Notepad++中提供了“匹配新行”功能,如果我们选中了,那么就会匹配成功第3步所讲的字符串。

关于NotePad++中的查找模式以及符号
Word、Excel、txt等文字编辑文件中有一些平时不可见的符号,如制表符、换行符、软回车等。当我们需要替换或者批量删除这些特殊的符号的时候,NotePad++就是一个很强大的工具了。

\t:制表符。在NotePad++中显示为向右的箭头
\n:换行符。在NotePad++中显示为LF
\r:软回车。在NotePad++中显示为CR
\0:??
\x:??
————————————————
版权声明:本文为CSDN博主「bcbobo21cn」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/bcbobo21cn/article/details/51803773

sql server 2008 r2 双机热备操作流程_lance_kl的博客-CSDN博客

mikel阅读(741)

来源: sql server 2008 r2 双机热备操作流程_lance_kl的博客-CSDN博客

一、配置步骤:

1.两台服务器,主服务器,从服务器,两台服务器都安装SQL Server 2008R2并且安装相同表结构的数据库(需要实时同步的数据库)

2.主服务器数据库安装完毕,打开SQL Server 配置管理器,把SQL Server 代理服务打开(已打开请忽略)

3.登录主服务器数据库,从数据库左侧菜单栏找到->复制->本地发布,右击新建发布

4.默认主服务器当作分发服务器

5.快照文件夹最好新建一个目录(需要手动在D盘新建目录),默认目录会有权限问题(对于新手权限问题不好解决)

6.选择需要发布的数据库

7.发布类型选择事务发布(发布类型的区别可自行百度,此处不做叙述)

8.选择需要发布的表(需要同步到从服务器数据库表的数据)

9.筛选表行(意思是表中的哪些字段你不需要同步到从服务器)

10.快照代理,选择->立即创建快照并使快照保持可用状态,以初始化订阅

11.代理安全性,点击安全设置,按照红色框中设置即可

12.向导操作,选择->创建发布->下一步

13.完成该向导,填写一个发布名称,点击完成,到此步骤主服务器本地订阅发布已经完成

14.登录从服务器数据库,从数据库左侧菜单栏找到->复制->本地订阅->右击新建订阅->选择查找SQL Server 发布服务器,数据库服务器名称要是主服务器计算机名称,输入登录名和密码

15.选择刚才主服务器发布的订阅

16.选择运行分发代理的位置,勾选第一个,如果选择第二个也需要开启从服务器SQL Server代理服务

17.选择订阅服务器(从服务器),订阅数据库需要同步的数据库(主服务器和从服务器热备份的数据库结构需要一致)

18.分发代理安全性,推荐按红色框中设置,如下图:(注意这里的账号密码,为相应服务器的实际账户密码)

19.同步计划选择连续运行

20.初始化订阅

21.向导操作->创建订阅->下一步->完成

22.刷新从服务器左侧菜单栏下的本地订阅,会出现刚刚创建好的订阅,为了验证设置是否成功,右击创建好的订阅->查看同步状态,如图显示同步正在进行则说明双机热备份设置成功

23.测试配置是否生效,在主服务器数据库通过updata语句修改某个表的字段值,然后查看从服务器数据库是否对应修改即可

 

 

二、SQL Server发布订阅异常处理

1.强制删除发布命令:EXEC SP_REMOVEDBREPLICATION ‘发布数据库名称’;

2.SQL2008发布订阅报错“进程无法在WIN-SERVER 上执行 sp_replcmds”

执行以下SQL脚本

①.首先执行: ALTER AUTHORIZATION ON DATABASE::[数据库名] TO [SQL登录用户名]

②.然后执行:

USE 数据库名

GO

sp_changedbowner ‘SQL登录用户名’

③.最后执行:

EXEC sp_repldone @xactid =NULL, @xact_segno =NULL, @numtrans = 0, @time =0, @reset =1

EXEC sp_replflush

④.如果还是不可以, 重启SQL Server Agent服务。

Log4Net 日志查看工具_随心-CSDN博客

mikel阅读(1167)

来源: Log4Net 日志查看工具_随心-CSDN博客

对于网站开发来说,如果我们采用了log4net来记录日志,那么查看日志最常用的方式就是直接去文件夹下面看,但这样做及其的不方便,于是乎借用log4net的 UdpAppender 开发了下面这样的一个工具用于实时查看日志。

该工具目前支持的功能如下:

支持同时收集多个APP的日志,并且提供筛选功能
支持按照线程ID进行筛选日志
支持通过日志名称筛选日志
支持一键清空显示区域的所有日志
支持自动滚动或者禁止自动滚动
支持置顶或者取消置顶
支持复制配置,复制配置之后粘贴到APP的配置文件中即可使用
支持自定义监听端口
支持调整窗口的透明度
支持调整显示区域的字体大小
界面如下:

 

相关代码已经开源,地址为:https://github.com/cxwl3sxl/LogViewer

程序下载:https://download.csdn.net/download/cxwl3sxl/10330711
————————————————
版权声明:本文为CSDN博主「cxwl3sxl」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cxwl3sxl/article/details/79835851

 SQL Server 2008 R2双机热备_wangqi791975的博客-CSDN博客

mikel阅读(793)

来源: (1条消息) SQL Server 2008 R2双机热备_wangqi791975的博客-CSDN博客

作用:
1、可以实施响应备份事务,备份时间间隔较短
2、当主数据库挂掉时可以切到备份数据库服务器,程序不会挂掉。
3、可以用于读写分离,增删改操作在主数据库服务器上进行,查询在备份数据库服务器上进行。一方面提高软件执行效率,另一方面也减轻主库压力。

操作:
1、需要2台分别安装SQL Server 2008R2的数据库服务器。
2、这里有3个名称需要搞清楚
发布服务器:主库所在的服务器。
分发服务器:用于传递当主库发生变化(增删改)时发送到订阅服务器的
订阅服务器:备份服务器
3、新建发布服务器:

 

 

 

4、新建订阅服务器

 

 

 

 

 

 

————————————————
版权声明:本文为CSDN博主「IngGoer」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wangqi791975/article/details/50208071

excel怎么删除指定行?Excel删除指定颜色的行的教程

mikel阅读(1667)

我们在使用Excel的时候,有时候需要删除一些单元格或者行,那么Excel中如何删除指定颜色的行呢?

1、我们在图示空白的单元格输入测试数据和一些带有颜色的行。

2、我们按下CTRL+F打开“找”窗口,或者在菜单栏依次点击“开始-查找/替换”。

3、接着我们在弹出的查找框内点击“选项-设置查找的格式”。

4、依次点击“格式-从单元格选择格式”,用吸管直接吸取黄色单元格。

5、接着我们点“全部查找”后,按ctrl+A即可全选所有黄色单元格。

6、最后右击弹出菜单栏,点击删除,选择整行即可。如图所示,已经删除了所有的黄色单元格。