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

1.什麼是jwt

雙方之間傳遞安全信息的簡潔的、URL安全的表述性聲明規範。JWT做爲一個開放的標準(RFC 7519),定義了一種簡潔的,自包含的方法用於通訊雙方之間以Json對象的形式安全的傳遞信息。簡潔(Compact): 能夠經過URL,POST參數或者在HTTP header發送,由於數據量小,傳輸速度也很快 自包含(Self-contained):負載中包含了全部用戶所須要的信息,避免了屢次查詢數據庫。html

 

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

第一步:引入maven依賴java

複製代碼

<!--引入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>

複製代碼

第二步:建立兩個註解,攔截器經過註釋區分是否進行權限攔截web

 

複製代碼

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

}

複製代碼

 

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

 

複製代碼

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中的實際應用json

 

複製代碼

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 "你已經過驗證";
    }


}

複製代碼

 

 

最後一步,咱們如今來訪問一下啊api

 

先進行登陸安全

 

登陸後攜帶token進行業務操做springboot

 

簡單的Demo程序就到這裏了,固然還有不少東西沒有考慮到,好比jwt在集羣環境下的應用等一些問題留到下回探討啦~

 

 

雜家不如專家,精益求精

相關文章
相關標籤/搜索