java不同项目加token访问_Java后端生成Token架构与设计详解

1.概述:在web项目中,服务端和前端经常需要交互数据,有的时候由于网络相应慢,客户端在提交某些敏感数据(比如按照正常的业务逻辑,此份数据只能保存一份)时,如果前端多次点击提交按钮会导致提交多份数据,这种情况我们是要防止发生的。

2.解决方法:

①前端处理:在提交之后通过js立即将按钮隐藏或者置为不可用。

②后端处理:对于每次提交到后台的数据必须校验,也就是通过前端携带的令牌(一串唯一字符串)与后端校验来判断当前数据是否有效。

3.总结:第一种方法相对来说比较简单,但是安全系数不高,第二种方法从根本上解决了问题,所以我推荐第二种方法。

4.核心代码:

生成Token的工具类:

  1. /**
  2. * 生成Token的工具类:
  3. */
  4. package red.hearing.eval.modules.token;
  5. import java.security.MessageDigest;
  6. import java.security.NoSuchAlgorithmException;
  7. import java.util.Random;
  8. import sun.misc.BASE64Encoder;
  9. /**
  10. * 生成Token的工具类
  11. *
  12. */
  13. public class TokenProccessor {
  14. private TokenProccessor(){};
  15. private static final TokenProccessor instance = new TokenProccessor();
  16. public static TokenProccessor getInstance() {
  17. return instance;
  18. }
  19. /**
  20. * 生成Token
  21. * @return
  22. */
  23. public String makeToken() {
  24. String token = (System.currentTimeMillis() + new Random().nextInt(999999999)) + "";
  25. try {
  26. MessageDigest md = MessageDigest.getInstance("md5");
  27. byte md5[] = md.digest(token.getBytes());
  28. BASE64Encoder encoder = new BASE64Encoder();
  29. return encoder.encode(md5);
  30. } catch (NoSuchAlgorithmException e) {
  31. // TODO Auto-generated catch block
  32. e.printStackTrace();
  33. }
  34. return null;
  35. }
  36. }

Token通用工具类

  1. /**
  2. *
  3. */
  4. package red.hearing.eval.modules.token;
  5. import javax.servlet.http.HttpServletRequest;
  6. import org.apache.commons.lang3.StringUtils;
  7. /**
  8. * Token的工具类
  9. *
  10. */
  11. public class TokenTools {
  12. /**
  13. * 生成token放入session
  14. * @param request
  15. * @param tokenServerkey
  16. */
  17. public static void createToken(HttpServletRequest request,String tokenServerkey){
  18. String token = TokenProccessor.getInstance().makeToken();
  19. request.getSession().setAttribute(tokenServerkey, token);
  20. }
  21. /**
  22. * 移除token
  23. * @param request
  24. * @param tokenServerkey
  25. */
  26. public static void removeToken(HttpServletRequest request,String tokenServerkey){
  27. request.getSession().removeAttribute(tokenServerkey);
  28. }
  29. /**
  30. * 判断请求参数中的token是否和session中一致
  31. * @param request
  32. * @param tokenClientkey
  33. * @param tokenServerkey
  34. * @return
  35. */
  36. public static boolean judgeTokenIsEqual(HttpServletRequest request,String tokenClientkey,String tokenServerkey){
  37. String token_client = request.getParameter(tokenClientkey);
  38. if(StringUtils.isEmpty(token_client)){
  39. return false;
  40. }
  41. String token_server = (String) request.getSession().getAttribute(tokenServerkey);
  42. if(StringUtils.isEmpty(token_server)){
  43. return false;
  44. }
  45. if(!token_server.equals(token_client)){
  46. return false;
  47. }
  48. return true;
  49. }
  50. }

使用方法:

①在输出前端页面的时候调用TokenTools.createToken方法,会把本次生成的token放入session中。

②然后在前端页面提交数据时从session中获取token,然后添加到要提交的数据中。

③服务端接受数据后调用judgeTokenIsEqual方法判断两个token是否一致,如果不一致则返回,不进行处理。

备注:tokenClientkey和tokenServerkey自定义,调用judgeTokenIsEqual方法时的tokenClientkey一定要与前端页面的key一致。

基于微信原理JAVA实现线程安全Token验证-JWT,如果不清楚JWT TOKEN的原理机制,我的上一篇JWT-TOKEN博客有详细介绍,这篇博文主要是具体实现。

Token主要是用于以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上密匙。

e81c941c2b8ed86b48859c71dcae35d7.gif


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管理*/ public class OAuthTokenManager { private String APP_ID = ""; private String APP_SECRET = ""; private String KEY_SING = ""; //用於存放TOKEN的標誌,Redis private LinkedHashMap<string, object=""> pairs = new LinkedHashMap();//封装json的map private CommonService service; public static final int MINUTE_TTL = 60*1000; //millisecond public static final int HOURS_TTL = 60*60*1000; //millisecond public static final int DAY_TTL = 12*60*60*1000; //millisecond private 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.key pairs.put(key, value); } public void remove(String key){ pairs.remove(key); } /** */ public String token(String appid,String secret,LogicInterface logicInterface){ //获取appid和secret this.accessPairs(appid,secret); //验证appid和secretS,获取对象载体 Object subject = this.loginAuthentication(logicInterface); //生成JWT签名数据ToKen String 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; } }

e81c941c2b8ed86b48859c71dcae35d7.gif

e81c941c2b8ed86b48859c71dcae35d7.gif


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*/ @Scope("prototype") @Controller @RequestMapping("/oAuthToken") public class OAuthToken { /** * 獲取Token * @param grant_type * @param appid * @param secret * @return */ @RequestMapping(params = "token",method = RequestMethod.GET) @ResponseBody public 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是业务逻辑回掉函数 //返回Token map.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_token break; } return map; } @RequestMapping(params = "loginAuth2",method = RequestMethod.GET) @ResponseBody public 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,這裡模擬是cookie Cookie 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");//改造簽名,或者無效的Token map.put("loginAuth","該Token無效");//改造簽名,或者無效的Token }catch (ExpiredJwtException e){ map.put("state","expired");//改造簽名,或者無效的Token map.put("loginAuth","Token已經過時"); }catch (Exception e) { e.printStackTrace(); map.put("state","fail"); } return map; } @RequestMapping(params = "index",method = RequestMethod.GET) @ResponseBody public 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; } }

e81c941c2b8ed86b48859c71dcae35d7.gif

<dependency> <groupid>io.jsonwebtoken</groupid> <artifactid>jjwt</artifactid> <version>0.7.0</version> </dependency>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值