AWT架构生成与设计Token
點擊上方?好好學java?,選擇?星標?公眾號
重磅資訊、干貨,第一時間送達
今日推薦:硬剛一周,3W字總結,一年的經驗告訴你如何準備校招!
個人原創100W+訪問量博客:點擊前往,查看更多
作者:巨人大哥 來源:cnblogs.com/jurendage/p/9219041.html序
目的:Java開源生鮮電商平臺-Java后端生成Token目的是為了用于校驗客戶端,防止重復提交.
技術選型:用開源的JWT架構。
1.概述:
在web項目中,服務端和前端經常需要交互數據,有的時候由于網絡相應慢,客戶端在提交某些敏感數據(比如按照正常的業務邏輯,此份數據只能保存一份)時,如果前端多次點擊提交按鈕會導致提交多份數據,這種情況我們是要防止發生的。
2.解決方法:
①前端處理:在提交之后通過js立即將按鈕隱藏或者置為不可用。
②后端處理:對于每次提交到后臺的數據必須校驗,也就是通過前端攜帶的令牌(一串唯一字符串)與后端校驗來判斷當前數據是否有效。
3.總結:
第一種方法相對來說比較簡單,但是安全系數不高,第二種方法從根本上解決了問題,所以我推薦第二種方法。
4.核心代碼:
生成Token的工具類:
/**??*?生成Token的工具類:??*/?? package?red.hearing.eval.modules.token;??import?java.security.MessageDigest;?? import?java.security.NoSuchAlgorithmException;?? import?java.util.Random;??import?sun.misc.BASE64Encoder;??/**??*?生成Token的工具類??*?@author?zhous??*?@since?2018-2-23?13:59:27??*??*/?? public?class?TokenProccessor?{??private?TokenProccessor(){};??private?static?final?TokenProccessor?instance?=?new?TokenProccessor();??public?static?TokenProccessor?getInstance()?{??return?instance;??}??/**??*?生成Token??*?@return??*/??public?String?makeToken()?{??String?token?=?(System.currentTimeMillis()?+?new?Random().nextInt(999999999))?+?"";??try?{??MessageDigest?md?=?MessageDigest.getInstance("md5");??byte?md5[]?=??md.digest(token.getBytes());??BASE64Encoder?encoder?=?new?BASE64Encoder();??return?encoder.encode(md5);??}?catch?(NoSuchAlgorithmException?e)?{??//?TODO?Auto-generated?catch?block??e.printStackTrace();??}??return?null;??}?? }Token通用工具類
/**??*???*/?? package?red.hearing.eval.modules.token;??import?javax.servlet.http.HttpServletRequest;??import?org.apache.commons.lang3.StringUtils;??/**??*?Token的工具類??*?@author?zhous??*??*/?? public?class?TokenTools?{??/**??*?生成token放入session??*?@param?request??*?@param?tokenServerkey??*/??public?static?void?createToken(HttpServletRequest?request,String?tokenServerkey){??String?token?=?TokenProccessor.getInstance().makeToken();??request.getSession().setAttribute(tokenServerkey,?token);??}??/**??*?移除token??*?@param?request??*?@param?tokenServerkey??*/??public?static?void?removeToken(HttpServletRequest?request,String?tokenServerkey){??request.getSession().removeAttribute(tokenServerkey);??}??/**??*?判斷請求參數中的token是否和session中一致??*?@param?request??*?@param?tokenClientkey??*?@param?tokenServerkey??*?@return??*/??public?static?boolean?judgeTokenIsEqual(HttpServletRequest?request,String?tokenClientkey,String?tokenServerkey){??String?token_client?=?request.getParameter(tokenClientkey);??if(StringUtils.isEmpty(token_client)){??return?false;??}??String?token_server?=?(String)?request.getSession().getAttribute(tokenServerkey);??if(StringUtils.isEmpty(token_server)){??return?false;??}??if(!token_server.equals(token_client)){??return?false;??}??return?true;??}??}使用方法:
①在輸出前端頁面的時候調用TokenTools.createToken方法,會把本次生成的token放入session中。
②然后在前端頁面提交數據時從session中獲取token,然后添加到要提交的數據中。
③服務端接受數據后調用judgeTokenIsEqual方法判斷兩個token是否一致,如果不一致則返回,不進行處理。
?備注:tokenClientkey和tokenServerkey自定義,調用judgeTokenIsEqual方法時的tokenClientkey一定要與前端頁面的key一致。
?Token主要是用于以作客戶端進行請求的一個令牌,當第一次登錄后,服務器生成一個Token便將此Token返回給客戶端,以后客戶端只需帶上這個Token前來請求數據即可,無需再次帶上密匙。
package?com.franz.websocket;import?com.franz.common.utils.StringUtils; import?com.franz.weixin.p3.oauth2.util.MD5Util; import?io.jsonwebtoken.*; import?net.sf.json.JSONObject; import?org.apache.commons.codec.binary.Base64; import?org.jeecgframework.core.common.service.CommonService;import?javax.crypto.SecretKey; import?javax.crypto.spec.SecretKeySpec; import?javax.servlet.http.Cookie; import?javax.servlet.http.HttpServletResponse; import?javax.xml.bind.DatatypeConverter; import?java.util.Date; import?java.util.HashMap; import?java.util.LinkedHashMap; import?java.util.Map;/***?OAuthTokenUtils*?Token管理*?@author?nizhigengvip@163.com*/ public?class?OAuthTokenManager?{private?String?APP_ID?=?"";private?String?APP_SECRET?=?"";private?String?KEY_SING?=??"";?//用於存放TOKEN的標誌,Redisprivate?LinkedHashMap<string,?object="">?pairs?=?new?LinkedHashMap();//封裝json的mapprivate?CommonService?service;public?static?final?int?MINUTE_TTL?=?60*1000;??//millisecondpublic?static?final?int?HOURS_TTL?=?60*60*1000;??//millisecondpublic?static?final?int?DAY_TTL?=?12*60*60*1000;??//millisecondprivate?OAuthTokenManager()?{}private?static?OAuthTokenManager?single=null;public?static?OAuthTokenManager?getInstance()?{if?(single?==?null)?{single?=?new?OAuthTokenManager();}return?single;}public?String?getKEY_SING()?{return?KEY_SING;}public?void?setPairs(LinkedHashMap<string,?object="">?pairs)?{this.pairs?=?pairs;}public?LinkedHashMap<string,?object="">?getPairs()?{return?pairs;}public?void?put(String?key,?Object?value){//向json中添加屬性,在js中訪問,請調用data.map.keypairs.put(key,?value);}public?void?remove(String?key){pairs.remove(key);}/***?總體封裝*?@param?appid*?@param?secret*?@param?logicInterface?回調函數*?@return*/public?String?token(String?appid,String?secret,LogicInterface?logicInterface){//獲取appid和secretthis.accessPairs(appid,secret);//驗證appid和secretS,獲取對象載體Object?subject?=?this.loginAuthentication(logicInterface);//生成JWT簽名數據ToKenString?token?=?this.createToken(this.generalSubject(subject),this.MINUTE_TTL);return?token;}public?void?accessPairs(String?APP_ID,?String?APP_SECRET)?{this.APP_ID?=?APP_ID;this.APP_SECRET?=?APP_SECRET;//this.KEY_SING?=?MD5Util.MD5Encode(APP_ID+"_"+APP_SECRET,?"UTF-8").toUpperCase();//要用到的時候才用}public?Object?loginAuthentication(LogicInterface?logicInterface){if?(StringUtils.isNotBlank(APP_ID)?&&?StringUtils.isNotBlank(APP_SECRET))?{Map<string,?object="">?map?=?new?HashMap<>();map.put("APP_ID",APP_ID);map.put("APP_SECRET",APP_SECRET);if(logicInterface?==?null?||?logicInterface.handler(map)?==?null){return?map;}else?{return?logicInterface.handler(map);}}?else?{return?null;}}/***?由字符串生成加密key*?@return*/public?SecretKey?generalKey(){String?stringKey?=?APP_ID+APP_SECRET;byte[]?encodedKey?=?Base64.decodeBase64(stringKey);SecretKey?key?=?new?SecretKeySpec(encodedKey,?0,?encodedKey.length,?"AES");return?key;}/***?生成subject信息*?@param?obj*?@return*/public?static?String?generalSubject(Object?obj){if(obj?!=?null?)?{JSONObject?json?=?JSONObject.fromObject(obj);return?json.toString();}else{return?"{}";}}/***?創建token*?@param?subject*?@param?ttlMillis*?@return*?@throws?Exception*/public?String?createToken(String?subject,?long?ttlMillis)?{SignatureAlgorithm?signatureAlgorithm?=?SignatureAlgorithm.HS256;long?nowMillis?=?System.currentTimeMillis();Date?now?=?new?Date(nowMillis);SecretKey?key?=?generalKey();JwtBuilder?builder?=?Jwts.builder().setId(APP_ID).setIssuedAt(now).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*?@return*?@throws?Exception*/public?Claims?validateToken(String?token)?throws?Exception{Claims?claims?=?Jwts.parser().setSigningKey(generalKey()).parseClaimsJws(token).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());*/return?claims;} } import?com.ewider.weixin.p3.oauth2.util.MD5Util; import?io.jsonwebtoken.Claims; import?io.jsonwebtoken.ExpiredJwtException; import?io.jsonwebtoken.MalformedJwtException; import?io.jsonwebtoken.SignatureException; import?org.springframework.context.annotation.Scope; import?org.springframework.stereotype.Controller; import?org.springframework.web.bind.annotation.RequestMapping; import?org.springframework.web.bind.annotation.RequestMethod; import?org.springframework.web.bind.annotation.RequestParam; import?org.springframework.web.bind.annotation.ResponseBody;import?javax.servlet.http.Cookie; import?javax.servlet.http.HttpServletRequest; import?javax.servlet.http.HttpServletResponse; import?java.text.SimpleDateFormat; import?java.util.Date; import?java.util.HashMap; import?java.util.Map;/***?OAuthTokenController**?@author?Franz.ge.倪志耿*/ @Scope("prototype") @Controller @RequestMapping("/oAuthToken") public?class?OAuthToken?{/***?獲取Token*?@param?grant_type*?@param?appid*?@param?secret*?@return*/@RequestMapping(params?=?"token",method?=?RequestMethod.GET)@ResponseBodypublic?Object?token?(@RequestParam(value?=?"grant_type")?String?grant_type,?@RequestParam(value?=?"appid")?String?appid,@RequestParam(value?=?"secret")?String?secret,HttpServletResponse?response)?{Map<string,?object="">?map?=?new?HashMap<>();switch?(grant_type)?{case?"authorization_code"?:?//授權碼模式(即先登錄獲取code,再獲取token)break;case?"password"?:?//密碼模式(將用戶名,密碼傳過去,直接獲取token)break;case?"client_credentials"?:?//客戶端模式(無用戶,用戶向客戶端注冊,然后客戶端以自己的名義向’服務端’獲取資源)OAuthTokenManager?oAuthTokenManager?=?OAuthTokenManager.getInstance();String?token?=?oAuthTokenManager.token(appid,?secret,null);//loginInterface是業務邏輯回掉函數//返回Tokenmap.put("access_token",token);map.put("expires_in",OAuthTokenManager.MINUTE_TTL/1000);break;case?"implicit"?:?//簡化模式(在redirect_uri?的Hash傳遞token;?Auth客戶端運行在瀏覽器中,如JS,Flash)break;case?"refresh_token"?:?//刷新access_tokenbreak;}return?map;}@RequestMapping(params?=?"loginAuth2",method?=?RequestMethod.GET)@ResponseBodypublic?Object?loginAuth2?(HttpServletRequest?request,?HttpServletResponse?response,?@RequestParam(value?=?"accessToken")?String?accessToken?){Map<string,?object="">?map?=?new?HashMap<>();//COOKIE不存在:解析驗證正確性try?{OAuthTokenManager?oAuthTokenManager?=?OAuthTokenManager.getInstance();Claims?claims?=?oAuthTokenManager.validateToken(accessToken);if?(claims?!=?null?)?{map.put("state","success");map.put("loginAuth","采用Token登錄");int?validMillis?=?(int)(claims.getExpiration().getTime()-System.currentTimeMillis());if(validMillis?>?0)?{//交給容器管理,可以存放redis,這裡模擬是cookieCookie?cookie?=?new?Cookie(MD5Util.MD5Encode("MD5SING",?"UTF-8").toUpperCase(),?accessToken);cookie.setMaxAge(validMillis/1000);response.addCookie(cookie);}}else{map.put("state","fail");}}catch?(MalformedJwtException?|?SignatureException?e){map.put("state","signature");//改造簽名,或者無效的Tokenmap.put("loginAuth","該Token無效");//改造簽名,或者無效的Token}catch?(ExpiredJwtException?e){map.put("state","expired");//改造簽名,或者無效的Tokenmap.put("loginAuth","Token已經過時");}catch?(Exception?e)?{e.printStackTrace();map.put("state","fail");}return?map;}@RequestMapping(params?=?"index",method?=?RequestMethod.GET)@ResponseBodypublic?Object?index?(HttpServletRequest?request,?HttpServletResponse?response){Map<string,?object="">?map?=?new?HashMap<>();//從COOKIE中查找,模擬訪問,可以集成容器管理Cookie[]?cookies?=?request.getCookies();if?(cookies!=null)?{for?(int?i?=?cookies.length-1;?i?>=?0;?i--)?{Cookie?cookie?=?cookies[i];if?(cookie.getName().equals(MD5Util.MD5Encode("MD5SING",?"UTF-8").toUpperCase()))?{//跳過登陸map.put("index","采用Redis登錄");return?map;}}}map.put("index","你的Token已經銷毀");return?map;}} <dependency><groupid>io.jsonwebtoken</groupid><artifactid>jjwt</artifactid><version>0.7.0</version> </dependency>推薦文章
硬剛一周,3W字總結,一年的經驗告訴你如何準備校招!
今年的校招,Java 好拿 offer 嗎?
10月了,該聊聊今年秋招了!
聊聊在騰訊實習快一個月的感受
總結
以上是生活随笔為你收集整理的AWT架构生成与设计Token的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 沸腾了!苏宁全员涨薪,每月最高多1万6!
- 下一篇: 面试官:为什么HTTPS是安全的