后端生成token架构与设计详解-mile米乐体育

目的: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的標誌,redis
    private linkedhashmap""> 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""> pairs) {
        this.pairs = pairs;
    }
    public linkedhashmap""> 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""> 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)
    @responsebody
    public object token (@requestparam(value = "grant_type") string grant_type, @requestparam(value = "appid") string appid,
            @requestparam(value = "secret") string secret,httpservletresponse response) 
{
        map""> 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""> 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""> 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.jsonwebtokengroupid>
  <artifactid>jjwtartifactid>
            <version>0.7.0version>
dependency>



展开全文
内容来源于互联网和用户投稿,文章中一旦含有米乐app官网登录的联系方式务必识别真假,本站仅做信息展示不承担任何相关责任,如有侵权或涉及法律问题请联系米乐app官网登录删除

最新文章

网站地图