Java生鲜电商平台-Java后端生成Token架构与设计详解

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

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

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. * @author zhous
  12. * @since 2018-2-23 13:59:27
  13. *
  14. */
  15. public class TokenProccessor {
  16. private TokenProccessor(){};
  17. private static final TokenProccessor instance = new TokenProccessor();
  18. public static TokenProccessor getInstance() {
  19. return instance;
  20. }
  21. /**
  22. * 生成Token
  23. * @return
  24. */
  25. public String makeToken() {
  26. String token = (System.currentTimeMillis() + new Random().nextInt(999999999)) + "";
  27. try {
  28. MessageDigest md = MessageDigest.getInstance("md5");
  29. byte md5[] =  md.digest(token.getBytes());
  30. BASE64Encoder encoder = new BASE64Encoder();
  31. return encoder.encode(md5);
  32. } catch (NoSuchAlgorithmException e) {
  33. // TODO Auto-generated catch block
  34. e.printStackTrace();
  35. }
  36. return null;
  37. }
  38. }

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. * @author zhous
  10. * @since 2018-2-23 14:01:41
  11. *
  12. */
  13. public class TokenTools {
  14. /**
  15. * 生成token放入session
  16. * @param request
  17. * @param tokenServerkey
  18. */
  19. public static void createToken(HttpServletRequest request,String tokenServerkey){
  20. String token = TokenProccessor.getInstance().makeToken();
  21. request.getSession().setAttribute(tokenServerkey, token);
  22. }
  23. /**
  24. * 移除token
  25. * @param request
  26. * @param tokenServerkey
  27. */
  28. public static void removeToken(HttpServletRequest request,String tokenServerkey){
  29. request.getSession().removeAttribute(tokenServerkey);
  30. }
  31. /**
  32. * 判断请求参数中的token是否和session中一致
  33. * @param request
  34. * @param tokenClientkey
  35. * @param tokenServerkey
  36. * @return
  37. */
  38. public static boolean judgeTokenIsEqual(HttpServletRequest request,String tokenClientkey,String tokenServerkey){
  39. String token_client = request.getParameter(tokenClientkey);
  40. if(StringUtils.isEmpty(token_client)){
  41. return false;
  42. }
  43. String token_server = (String) request.getSession().getAttribute(tokenServerkey);
  44. if(StringUtils.isEmpty(token_server)){
  45. return false;
  46. }
  47. if(!token_server.equals(token_client)){
  48. return false;
  49. }
  50. return true;
  51. }
  52. }

使用方法:

①在输出前端页面的时候调用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架构与设计详解的更多相关文章

  1. Java生鲜电商平台-订单配送模块的架构与设计

    Java生鲜电商平台-订单配送模块的架构与设计 生鲜电商系统最终的目的还是用户下单支付购买, 所以订单管理系统是电商系统中最为复杂的系统,其作为中枢决定着整个商城的运转, 本文将对于生鲜类电商平台的订 ...

  2. Java生鲜电商平台-高可用微服务系统如何设计?

    Java生鲜电商平台-高可用微服务系统如何设计? 说明:Java生鲜电商平台高可用架构往往有以下的要求: 高可用.这类的系统往往需要保持一定的 SLA,7*24 时不间断运行不代表完全不挂,而是有一定 ...

  3. Java生鲜电商平台-秒杀系统微服务架构设计与源码解析实战

    Java生鲜电商平台-秒杀系统微服务架构设计与源码解析实战 Java生鲜电商平台-  什么是秒杀 通俗一点讲就是网络商家为促销等目的组织的网上限时抢购活动 比如说京东秒杀,就是一种定时定量秒杀,在规定 ...

  4. Java生鲜电商平台-小程序或者APP优惠券的设计与源码实战

    Java生鲜电商平台-小程序或者APP优惠券的设计与源码实战 说明:Java生鲜电商平台-小程序或者APP优惠券的设计与源码实战,优惠券是一种常见的促销方式,在规定的周期内购买对应商品类型和额度的商品 ...

  5. Java生鲜电商平台-服务化后的互联网架构实战(针对生鲜电商小程序或者APP)

    Java生鲜电商平台-服务化后的互联网架构实战(针对生鲜电商小程序或者APP) “微服务架构”的话题非常之火,很多朋友都在小窗我,说怎么做服务化?解答“怎么做”之前,先得了解“为什么做”. 画外音:做 ...

  6. Java生鲜电商平台-深入订单拆单架构与实战

    Java生鲜电商平台-深入订单拆单架构与实战 Java生鲜电商中在做拆单的需求,细思极恐,思考越深入,就会发现里面涉及的东西越来越多,要想做好订单拆单的功能,还是相当有难度, 因此总结了一下拆单功能细 ...

  7. Java生鲜电商平台-redis缓存在商品中的设计与架构

    Java生鲜电商平台-redis缓存在商品中的设计与架构 说明:Java开源生鲜电商平台-redis缓存在商品中的设计与架构. 1. 各种计数,商品维度计数和用户维度计数 说起电商,肯定离不开商品,而 ...

  8. Java开源生鲜电商平台-性能优化以及服务器优化的设计与架构(源码可下载)

    Java开源生鲜电商平台-性能优化以及服务器优化的设计与架构(源码可下载) 说明:Java开源生鲜电商平台-性能优化以及服务器优化的设计与架构,我采用以下三种维度来讲解 1.  代码层面. 2.  数 ...

  9. Java生鲜电商平台-生鲜系统中微服务架构设计与分析实战

    Java生鲜电商平台-生鲜系统中微服务架构设计与分析实战 说明: Java生鲜系统中微服务的拆分应该如何架构设计与分析呢?以下是我的实战中的设计与经验分析. 目录 1. 微服务简介2. 当前现状3. ...

随机推荐

  1. PHP收集一些常用函数与好用的自定义函数

    .自定义打印函数P //自定义打印function pp($data,$exit=0){// 定义样式 $str='<pre style="display: block;padding ...

  2. opencv与mfc显示图片操作,MFC的鼠标响应在opencv图片上失效,opencv滚轮事件没有响应问题描述解决。

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/11588758.html 用mfc 与OPENCV编写成. 出了很多冲突异常事件导致鼠标操作没有 ...

  3. stars-one的原创工具——APK签名验证破解工具

    ASCTool APk签名验证破解工具 APK Signature Crack Tool 本工具只对那些仅通过 PackageManager.getPackageInfo().signatures 来 ...

  4. Java每日一面(Part1:计算机网络)[19/10/13]

    作者:晨钟暮鼓c个人微信公众号:程序猿的月光宝盒 1.说说TCP三次握手 1.0 在此之前,什么是TCP? ​ TCP(传输控制协议) ​ 1.面向连接的,可靠的,基于字节流的传输层通信协议 ​ 2. ...

  5. 理解并运用TP5.1-Facade

    1.内容介绍 深入解析tp5.1与laravel 中Facade底层原理实现 1. 什么是Facade 2. 为什么需要有什么好处 3.  Facade实现原理 4. 功能实现. 5. 容器注入 2. ...

  6. jwt认证生成后的token后端解析

    一.首先前端发送token token所在的位置headers {'authorization':token的值',Content-Type':application/json} 在ajax写 //只 ...

  7. thinkphp 5.x No input file specified 解决

    原规则: <IfModule mod_rewrite.c> Options +FollowSymlinks -Multiviews RewriteEngine On RewriteCond ...

  8. 编译原理之不懂就问-First集

    老师PPT: 这条语言实在是..通俗易懂

  9. 使用Anaconda3的Docker镜像

    假设本地 Ubuntu 服务器已经安装好了Docker,这里讲述一下如何开始运行Anaconda3的Docker镜像: 1. 搜索镜像 搜索我们想要的anaconda镜像: docker search ...

  10. 使用STS4新建springboot项目

    1.配置maven,自定义setting文件和仓库,一定要用阿里云镜像地址下载依赖,官方太坑了,整了半天都没弄好,原来是下载太慢文件损坏 <mirror> <id>alimav ...