Jwt在Java項目中的簡單實際應用


1.什么是jwt

雙方之間傳遞安全信息的簡潔的、URL安全的表述性聲明規范。JWT作為一個開放的標准( RFC 7519),定義了一種簡潔的,自包含的方法用於通信雙方之間以Json對象的形式安全的傳遞信息。 簡潔(Compact): 可以通過URL,POST參數或者在HTTP header發送,因為數據量小,傳輸速度也很快  自包含(Self-contained):負載中包含了所有用戶所需要的信息,避免了多次查詢數據庫。
 

2.Jwt在javaweb項目中的簡單使用

第一步:引入maven依賴

<!--引入JWT依賴,由於是基於Java,所以需要的是java-jwt-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>

第二步:創建兩個注解,攔截器通過注釋區分是否進行權限攔截(詳情參考上一篇文章JWT在JAVAWEB項目中的應用核心步驟解讀 https://www.cnblogs.com/jimisun/p/9480886.html)

package com.pjb.springbootjjwt.jimisun;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginToken {
    boolean required() default true;
}
package com.pjb.springbootjjwt.jimisun;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckToken {
    boolean required() default true;
}

 

第三步:編寫JwtUtil工具類(生成token,解析token,校驗token)

package com.pjb.springbootjjwt.jimisun;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @Author:jimisun
 * @Description:
 * @Date:Created in 14:08 2018/8/15
 * @Modified By:
 */
public class JwtUtil {

    /**
     * 用戶登錄成功后生成Jwt
     * 使用Hs256算法  私匙使用用戶密碼
     *
     * @param ttlMillis jwt過期時間
     * @param user      登錄成功的user對象
     * @return
     */
    public static String createJWT(long ttlMillis, User user) {
        //指定簽名的時候使用的簽名算法,也就是header那部分,jjwt已經將這部分內容封裝好了。
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        //生成JWT的時間
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        //創建payload的私有聲明(根據特定的業務需要添加,如果要拿這個做驗證,一般是需要和jwt的接收方提前溝通好驗證方式的)
        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put("id", user.getId());
        claims.put("username", user.getUsername());
        claims.put("password", user.getPassword());

        //生成簽名的時候使用的秘鑰secret,這個方法本地封裝了的,一般可以從本地配置文件中讀取,切記這個秘鑰不能外露哦。它就是你服務端的私鑰,在任何場景都不應該流露出去。一旦客戶端得知這個secret, 那就意味着客戶端是可以自我簽發jwt了。
        String key = user.getPassword();

        //生成簽發人
        String subject = user.getUsername();



        //下面就是在為payload添加各種標准聲明和私有聲明了
        //這里其實就是new一個JwtBuilder,設置jwt的body
        JwtBuilder builder = Jwts.builder()
                //如果有私有聲明,一定要先設置這個自己創建的私有的聲明,這個是給builder的claim賦值,一旦寫在標准的聲明賦值之后,就是覆蓋了那些標准的聲明的
                .setClaims(claims)
                //設置jti(JWT ID):是JWT的唯一標識,根據業務需要,這個可以設置為一個不重復的值,主要用來作為一次性token,從而回避重放攻擊。
                .setId(UUID.randomUUID().toString())
                //iat: jwt的簽發時間
                .setIssuedAt(now)
                //代表這個JWT的主體,即它的所有人,這個是一個json格式的字符串,可以存放什么userid,roldid之類的,作為什么用戶的唯一標志。
                .setSubject(subject)
                //設置簽名使用的簽名算法和簽名使用的秘鑰
                .signWith(signatureAlgorithm, key);
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            //設置過期時間
            builder.setExpiration(exp);
        }
        return builder.compact();
    }


    /**
     * Token的解密
     * @param token 加密后的token
     * @param user  用戶的對象
     * @return
     */
    public static Claims parseJWT(String token, User user) {
        //簽名秘鑰,和生成的簽名的秘鑰一模一樣
        String key = user.getPassword();

        //得到DefaultJwtParser
        Claims claims = Jwts.parser()
                //設置簽名的秘鑰
                .setSigningKey(key)
                //設置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }


    /**
     * 校驗token
     * 在這里可以使用官方的校驗,我這里校驗的是token中攜帶的密碼於數據庫一致的話就校驗通過
     * @param token
     * @param user
     * @return
     */
    public static Boolean isVerify(String token, User user) {
        //簽名秘鑰,和生成的簽名的秘鑰一模一樣
        String key = user.getPassword();

        //得到DefaultJwtParser
        Claims claims = Jwts.parser()
                //設置簽名的秘鑰
                .setSigningKey(key)
                //設置需要解析的jwt
                .parseClaimsJws(token).getBody();

        if (claims.get("password").equals(user.getPassword())) {
            return true;
        }

        return false;
    }

}

 

第四步:編寫攔截器攔截請求進行權限驗證

package com.pjb.springbootjjwt.interceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.pjb.springbootjjwt.jimisun.CheckToken;
import com.pjb.springbootjjwt.jimisun.JwtUtil;
import com.pjb.springbootjjwt.jimisun.LoginToken;
import com.pjb.springbootjjwt.jimisun.User;
import com.pjb.springbootjjwt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;


/**
 * jimisun
 */
public class AuthenticationInterceptor implements HandlerInterceptor {

    @Autowired
    UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {

        // 從 http 請求頭中取出 token
        String token = httpServletRequest.getHeader("token");
        // 如果不是映射到方法直接通過
        if (!(object instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        //檢查是否有LoginToken注釋,有則跳過認證
        if (method.isAnnotationPresent(LoginToken.class)) {
            LoginToken loginToken = method.getAnnotation(LoginToken.class);
            if (loginToken.required()) {
                return true;
            }
        }

        //檢查有沒有需要用戶權限的注解
        if (method.isAnnotationPresent(CheckToken.class)) {
            CheckToken checkToken = method.getAnnotation(CheckToken.class);
            if (checkToken.required()) {
                // 執行認證
                if (token == null) {
                    throw new RuntimeException("無token,請重新登錄");
                }
                // 獲取 token 中的 user id
                String userId;
                try {
                    userId = JWT.decode(token).getClaim("id").asString();
                } catch (JWTDecodeException j) {
                    throw new RuntimeException("訪問異常!");
                }
                User user = userService.findUserById(userId);
                if (user == null) {
                    throw new RuntimeException("用戶不存在,請重新登錄");
                }
                Boolean verify = JwtUtil.isVerify(token, user);
                if (!verify) {
                    throw new RuntimeException("非法訪問!");
                }
                return true;
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }


}
配置攔截器(ps:我使用的是springboot,大家使用ssm配置攔截器的方式不一樣)

package com.pjb.springbootjjwt.interceptorconfig;

import com.pjb.springbootjjwt.interceptor.AuthenticationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class InterceptorConfig implements WebMvcConfigurer {


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**");    // 攔截所有請求,通過判斷是否有 @LoginRequired 注解 決定是否需要登錄
    }

    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor();
    }
}

第五步:在示例Controller中的實際應用

package com.pjb.springbootjjwt.jimisun;

import com.alibaba.fastjson.JSONObject;
import com.pjb.springbootjjwt.annotation.PassToken;
import com.pjb.springbootjjwt.annotation.UserLoginToken;
import com.pjb.springbootjjwt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.UUID;

/**
 * @Author:jimisun
 * @Description:
 * @Date:Created in 15:04 2018/8/15
 * @Modified By:
 */
@RestController
@RequestMapping("/api")
public class UserController {

    @Autowired
    private UserService userService;

    //登錄
    @PostMapping("/login")
    @LoginToken
    public Object login(@RequestBody @Valid com.pjb.springbootjjwt.jimisun.User user) {

        JSONObject jsonObject = new JSONObject();
        com.pjb.springbootjjwt.jimisun.User userForBase = userService.findByUsername(user);
        if (userForBase == null) {
            jsonObject.put("message", "登錄失敗,用戶不存在");
            return jsonObject;
        } else {
            if (!userForBase.getPassword().equals(user.getPassword())) {
                jsonObject.put("message", "登錄失敗,密碼錯誤");
                return jsonObject;
            } else {
                String token = JwtUtil.createJWT(6000000, userForBase);
                jsonObject.put("token", token);
                jsonObject.put("user", userForBase);
                return jsonObject;
            }
        }
    }

    //查看個人信息
    @CheckToken
    @GetMapping("/getMessage")
    public String getMessage() {
        return "你已通過驗證";
    }


}
 
 

最后一步,我們現在來訪問一下啊

先進行登錄

 

登錄后攜帶token進行業務操作

 

簡單的Demo程序就到這里了,當然還有很多東西沒有考慮到,比如jwt在集群環境下的應用等一些問題留到下回探討啦~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM