在SpringBoot中使用JWT

JWT簡介

簡介

JSON Web token簡稱JWT轩缤, 是用于對應(yīng)用程序上的用戶進(jìn)行身份驗證的標(biāo)記疮薇。也就是說, 使用 JWTS 的應(yīng)用程序不再需要保存有關(guān)其用戶的 cookie 或其他session數(shù)據(jù)情萤。此特性便于可伸縮性, 同時保證應(yīng)用程序的安全。

在身份驗證過程中, 當(dāng)用戶使用其憑據(jù)成功登錄時, 將返回 JSON Web token, 并且必須在本地保存 (通常在本地存儲中)抑月。每當(dāng)用戶要訪問受保護(hù)的路由或資源 (端點) 時, 用戶代理(user agent)必須連同請求一起發(fā)送 JWT, 通常在授權(quán)標(biāo)頭中使用Bearer schema幼衰。后端服務(wù)器接收到帶有 JWT 的請求時, 首先要做的是驗證token。

JWT的格式

JWT就是一個字符串诊霹,經(jīng)過加密處理與校驗處理的字符串羞延,形式為:A.B.C
A由JWT頭部信息header加密得到
B由JWT用到的身份驗證信息json數(shù)據(jù)加密得到
C由A和B加密得到,是校驗部分

怎樣使用token脾还?

可以放到HTTP請求的請求頭中伴箩,通常是Authorization字段。

流程圖

jwt流程圖.png

JWT 實戰(zhàn)

加入Maven jwt 依賴

<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.9.1</version>
</dependency>

在application.proterties中加入配置

# 加密yan
jwt.secret=A0B1C2D3E4F5G6H7I8J9KALBMCNDOEPFQ0R1S2T3U4V5W6X7Y8Z9
# tocken 過期時間鄙漏,單位秒
jwt.expire=300
# 需要認(rèn)證的url赛蔫,多個URL使用英文逗號,分割
jwt.authorised-urls=/apis/fis/redis/**

JwtHelper工具類

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class JwtHelper {
    private Long EXPIRATION_TIME;
    private String SECRET;
    private final String TOKEN_PREFIX = "Bearer";
    private final String HEADER_STRING = "Authorization";

    public JwtHelper(String secret, long expire) {
        this.EXPIRATION_TIME = expire;
        this.SECRET = secret;
        System.out.println("正在初始化Jwthelper,expire="+expire);
    }

    public JSONObject generateToken(Map<String, Object> claims) {
        Calendar c = Calendar.getInstance();
        c.setTime(new Date());
        c.add(Calendar.SECOND, EXPIRATION_TIME.intValue());
        Date d = c.getTime();
        String jwt = Jwts.builder()
                .setClaims(claims)
                .setExpiration(d)
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();
        JSONObject json = new JSONObject();
        json.put("token",TOKEN_PREFIX + " " + jwt);
        json.put("token-type", TOKEN_PREFIX);
        json.put("expire-time",new SimpleDateFormat("yyyy-MM-dd HH:ss:mm").format(d) );
        return json;
    }

    public Map<String, Object> validateTokenAndGetClaims(HttpServletRequest request) {
        String token = request.getHeader(HEADER_STRING);
        System.out.println("token is:"+token);
        if (token == null) {
            return null;
        } 
        Map<String, Object> body = Jwts.parser()
                .setSigningKey(SECRET)
                .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                .getBody();
        return body;
    }
}

JWT過濾器JwtFilter

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.util.AntPathMatcher;

/**
 * JWT過濾器
 * 
 * @author 李慶海
 *
 */
public class JwtFilter implements Filter {
    private JwtHelper jwtHelper;
    private List<String> urls = null;
     private static final org.springframework.util.PathMatcher pathMatcher = new AntPathMatcher();
    public JwtFilter(JwtHelper jwtHelper, String[] authorisedUrls) {
        this.jwtHelper = jwtHelper;
        urls = Arrays.asList(authorisedUrls);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        //SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, filterConfig.getServletContext());
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setCharacterEncoding("UTF-8");
        httpResponse.setContentType("application/json; charset=utf-8");
        httpResponse.setHeader("Access-Control-Allow-Origin", "*");
        if ("OPTIONS".equals(httpRequest.getMethod())) {
            httpResponse.setStatus(HttpStatus.NO_CONTENT.value()); // HttpStatus.SC_NO_CONTENT = 204
            httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
            httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, x-requested-with, Token");
            httpResponse.setHeader("Access-Control-Allow-Methods", "OPTIONS,GET,POST,DELETE,PUT");
        }
        String spath = httpRequest.getServletPath();
        try {
            // 驗證受保護(hù)的接口
            for (String url : urls) {
                if (pathMatcher.match(url, spath)) {
                    Object token = jwtHelper.validateTokenAndGetClaims(httpRequest);
                    if (token != null) {
                        chain.doFilter(request, response);
                        return;
                    }else{
                         httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "未授權(quán)或者授權(quán)已經(jīng)過期");
                         return;
                    }
                }else{
                    chain.doFilter(request, response);
                    return;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        chain.doFilter(request, response);
        return;
    }

    @Override
    public void destroy() {

    }
}

配置JWT

import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import cn.com.yd.fis.client.jwt.JwtFilter;
import cn.com.yd.fis.client.jwt.JwtHelper;

@Configuration
public class JwtConfig {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expire}")
    private long expire;

    @Value("${jwt.authorised-urls}")
    private String[] authorisedUrls;

    @Bean
    public JwtHelper jwtHelperBean() {
        return new JwtHelper(secret, expire);
    }

    @Bean
    public FilterRegistrationBean basicFilterRegistrationBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        JwtFilter filter = new JwtFilter(jwtHelperBean(), authorisedUrls);
        registrationBean.setFilter(filter);
        List<String> urlPatterns = new ArrayList<String>();
        urlPatterns.add("/*");
        registrationBean.setUrlPatterns(urlPatterns);
        return registrationBean;
    }
}

在Controller中使用JWT

此處僅為說明jwt的用法泥张,在實際應(yīng)用時可以根據(jù)具體的業(yè)務(wù)需要加入不同的或者更多的參數(shù)呵恢,一并作為claims進(jìn)行參數(shù)傳遞。

import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import cn.com.yd.fis.client.jwt.JwtHelper;
import cn.com.yd.fis.client.util.JsonResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;

@RestController
@RequestMapping("${api-url}/auth")
public class AuthorizeController {

    @Autowired
    private JwtHelper jwtHelper;

    @PostMapping("/login")
    public Object login(String loginName,String password) throws Exception {
        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put("loginName", loginName);
        if ("1".equals(password)) {
            return JsonResult.success(jwtHelper.generateToken(claims));
        } else {
            return JsonResult.fail("登錄帳號或者登錄密碼錯誤");
        }
    }
}

輔助工具類JsonResult

import com.alibaba.fastjson.JSONObject;

public class JsonResult {
    public static JSONObject success(Object obj) {
        JSONObject json = new JSONObject();
        json.put("state", true);
        json.put("msg", "成功");
        if (null != obj) {
            json.put("obj", obj);
        }
        return json;
    }

    public static JSONObject fail(Object obj) {
        JSONObject json = new JSONObject();
        json.put("state", false);
        json.put("msg", "失敗");
        if (null != obj) {
            json.put("obj", obj);
        }
        return json;
    }

    public static JSONObject toJSONObject(boolean state, String msg, Object obj) {
        JSONObject json = new JSONObject();
        json.put("state", state);
        json.put("msg", msg);
        if (null != obj) {
            json.put("obj", obj);
        }
        return json;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末媚创,一起剝皮案震驚了整個濱河市渗钉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖鳄橘,帶你破解...
    沈念sama閱讀 210,835評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件声离,死亡現(xiàn)場離奇詭異,居然都是意外死亡瘫怜,警方通過查閱死者的電腦和手機(jī)术徊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,900評論 2 383
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鲸湃,“玉大人赠涮,你說我怎么就攤上這事“堤簦” “怎么了笋除?”我有些...
    開封第一講書人閱讀 156,481評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長炸裆。 經(jīng)常有香客問我垃它,道長,這世上最難降的妖魔是什么烹看? 我笑而不...
    開封第一講書人閱讀 56,303評論 1 282
  • 正文 為了忘掉前任国拇,我火速辦了婚禮,結(jié)果婚禮上惯殊,老公的妹妹穿的比我還像新娘酱吝。我一直安慰自己,他們只是感情好靠胜,可當(dāng)我...
    茶點故事閱讀 65,375評論 5 384
  • 文/花漫 我一把揭開白布掉瞳。 她就那樣靜靜地躺著毕源,像睡著了一般浪漠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上霎褐,一...
    開封第一講書人閱讀 49,729評論 1 289
  • 那天址愿,我揣著相機(jī)與錄音,去河邊找鬼冻璃。 笑死响谓,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的省艳。 我是一名探鬼主播娘纷,決...
    沈念sama閱讀 38,877評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼跋炕!你這毒婦竟也來了赖晶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,633評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎遏插,沒想到半個月后捂贿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,088評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡胳嘲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,443評論 2 326
  • 正文 我和宋清朗相戀三年厂僧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片了牛。...
    茶點故事閱讀 38,563評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡颜屠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出白魂,到底是詐尸還是另有隱情汽纤,我是刑警寧澤,帶...
    沈念sama閱讀 34,251評論 4 328
  • 正文 年R本政府宣布福荸,位于F島的核電站蕴坪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏敬锐。R本人自食惡果不足惜背传,卻給世界環(huán)境...
    茶點故事閱讀 39,827評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望台夺。 院中可真熱鬧径玖,春花似錦、人聲如沸颤介。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,712評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽滚朵。三九已至冤灾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間辕近,已是汗流浹背韵吨。 一陣腳步聲響...
    開封第一講書人閱讀 31,943評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留移宅,地道東北人归粉。 一個月前我還...
    沈念sama閱讀 46,240評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像漏峰,于是被迫代替她去往敵國和親糠悼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,435評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 一個平靜的中午浅乔,我和以前一樣倔喂,平平淡淡地走進(jìn)教室。忽然,平靜的氣氛被打亂了滴劲,原來老師要跟我們玩一個好玩的游戲攻晒,吃雞...
    2e45075fd084閱讀 563評論 4 1
  • 【約13-15】我給你們作了榜樣,叫你們照著我向你們所作的去作班挖。 埃洛希姆上帝為了教育兒女們鲁捏,親自蒞臨世上,處處作...
    碧羅思愛閱讀 373評論 0 1