Java开源生鲜电商平台-Java后端生成Token架构与设计详解(源码可下载)

 andlly   2018-06-23 23:31   48 人阅读  0 条评论

目的:Java开源生鲜电商平台-Java后端生成Token目的是为了用于校验客户端,防止重复提交.

技术选型:用开源的JWT架构。

 

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

2.解决方法:

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

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

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

4.核心代码:

生成Token的工具类:

  1. /**  

  2.  * 生成Token的工具类:  

  3.  */  

  4. package red.hearing.eval.modules.token;  

  5.   

  6. import java.security.MessageDigest;  

  7. import java.security.NoSuchAlgorithmException;  

  8. import java.util.Random;  

  9.   

  10. import sun.misc.BASE64Encoder;  

  11.   

  12. /**  

  13.  * 生成Token的工具类  

  14.  * @author zhous  

  15.  * @since 2018-2-23 13:59:27  

  16.  *  

  17.  */  

  18. public class TokenProccessor {  

  19.       

  20.      private TokenProccessor(){};  

  21.      private static final TokenProccessor instance = new TokenProccessor();  

  22.        

  23.     public static TokenProccessor getInstance() {  

  24.         return instance;  

  25.     }  

  26.   

  27.     /**  

  28.      * 生成Token  

  29.      * @return  

  30.      */  

  31.     public String makeToken() {  

  32.         String token = (System.currentTimeMillis() + new Random().nextInt(999999999)) + "";  

  33.          try {  

  34.             MessageDigest md = MessageDigest.getInstance("md5");  

  35.             byte md5[] =  md.digest(token.getBytes());  

  36.             BASE64Encoder encoder = new BASE64Encoder();  

  37.             return encoder.encode(md5);  

  38.         } catch (NoSuchAlgorithmException e) {  

  39.             // TODO Auto-generated catch block  

  40.             e.printStackTrace();  

  41.         }  

  42.          return null;  

  43.     }  

  44. }  

Token通用工具类

 
  1. /**  

  2.  *   

  3.  */  

  4. package red.hearing.eval.modules.token;  

  5.   

  6. import javax.servlet.http.HttpServletRequest;  

  7.   

  8. import org.apache.commons.lang3.StringUtils;  

  9.   

  10. /**  

  11.  * Token的工具类  

  12.  * @author zhous  

  13.  * @since 2018-2-23 14:01:41  

  14.  *  

  15.  */  

  16. public class TokenTools {  

  17.       

  18.     /**  

  19.      * 生成token放入session  

  20.      * @param request  

  21.      * @param tokenServerkey  

  22.      */  

  23.     public static void createToken(HttpServletRequest request,String tokenServerkey){  

  24.         String token = TokenProccessor.getInstance().makeToken();  

  25.         request.getSession().setAttribute(tokenServerkey, token);  

  26.     }  

  27.       

  28.     /**  

  29.      * 移除token  

  30.      * @param request  

  31.      * @param tokenServerkey  

  32.      */  

  33.     public static void removeToken(HttpServletRequest request,String tokenServerkey){  

  34.         request.getSession().removeAttribute(tokenServerkey);  

  35.     }  

  36.       

  37.     /**  

  38.      * 判断请求参数中的token是否和session中一致  

  39.      * @param request  

  40.      * @param tokenClientkey  

  41.      * @param tokenServerkey  

  42.      * @return  

  43.      */  

  44.     public static boolean judgeTokenIsEqual(HttpServletRequest request,String tokenClientkey,String tokenServerkey){  

  45.         String token_client = request.getParameter(tokenClientkey);  

  46.         if(StringUtils.isEmpty(token_client)){  

  47.             return false;  

  48.         }  

  49.         String token_server = (String) request.getSession().getAttribute(tokenServerkey);  

  50.         if(StringUtils.isEmpty(token_server)){  

  51.             return false;  

  52.         }  

  53.           

  54.         if(!token_server.equals(token_client)){  

  55.             return false;  

  56.         }  

  57.           

  58.         return true;  

  59.     }  

  60.       

  61. }  

使用方法:

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

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

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

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

 

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

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
 * @version 2017-08-01 */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);
    } 
    /**
     * 總體封裝
     * @param appid
     * @param secret
     * @param logicInterface 回調函數
     * @return
     */
    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;
    }
}
复制代码
复制代码
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.倪志耿
 * @version 2017-08-01 */@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;
    }
 
}
复制代码
        <dependency>
            <groupid>io.jsonwebtoken</groupid>
           <artifactid>jjwt</artifactid>
            <version>0.7.0</version>
        </dependency>

 

Java开源生鲜电商平台-Java后端生成Token架构与设计详解(源码可下载),如果需要下载的话,可以在我的github下面进行下载。  

https://github.com/137071249/


本文地址:http://10tf.com/post/4.html
版权声明:本文为原创文章,版权归 andlly 所有,欢迎分享本文,转载请保留出处!

 发表评论


表情

还没有留言,还不快点抢沙发?