电商平台-Java后端生成Token架构与设计详解
目的: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
- * @since 2018-2-23 14:01:41
- *
- */
- 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一致。
基于微信原理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的標誌,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.倪志耿* @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)@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>
电商平台-Java后端生成Token架构与设计详解相关推荐
- 44、生鲜电商平台-Java后端生成Token架构与设计详解
暂无 转载于:https://www.cnblogs.com/lu-manman/p/10052474.html
- 后端生成Token架构与设计详解
点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:硬刚一周,3W字总结,一年的经验告诉你如何准备校招! 个人原创100W+访问量博客:点击前往,查看更多 作者:巨 ...
- Java生鲜电商平台-订单配送模块的架构与设计
Java生鲜电商平台-订单配送模块的架构与设计 生鲜电商系统最终的目的还是用户下单支付购买, 所以订单管理系统是电商系统中最为复杂的系统,其作为中枢决定着整个商城的运转, 本文将对于生鲜类电商平台的订 ...
- Java生鲜电商平台-秒杀系统微服务架构设计与源码解析实战
Java生鲜电商平台-秒杀系统微服务架构设计与源码解析实战 Java生鲜电商平台- 什么是秒杀 通俗一点讲就是网络商家为促销等目的组织的网上限时抢购活动 比如说京东秒杀,就是一种定时定量秒杀,在规定 ...
- Java生鲜电商平台-深入订单拆单架构与实战
Java生鲜电商平台-深入订单拆单架构与实战 Java生鲜电商中在做拆单的需求,细思极恐,思考越深入,就会发现里面涉及的东西越来越多,要想做好订单拆单的功能,还是相当有难度, 因此总结了一下拆单功能细 ...
- Java开源生鲜电商平台-Java分布式以及负载均衡架构与设计详解(源码可下载)
Java开源生鲜电商平台-Java分布式以及负载均衡架构与设计详解(源码可下载) 说明:主要是针对一些中大型的项目需要进行分布式以及负载均衡的架构提一些思路与建议. 面对大量用户访问.高并发请求,海量 ...
- Java生鲜电商平台-小程序或者APP优惠券的设计与源码实战
Java生鲜电商平台-小程序或者APP优惠券的设计与源码实战 说明:Java生鲜电商平台-小程序或者APP优惠券的设计与源码实战,优惠券是一种常见的促销方式,在规定的周期内购买对应商品类型和额度的商品 ...
- 架构设计-支付宝、京东、美团、去哪儿的支付系统架构整体设计详解!!!
架构设计-支付宝.京东.美团.去哪儿的支付系统架构整体设计详解!!! 支付产品模块是按照支付场景来为业务方提供支付服务.这个模块一般位于支付网关之后,支付渠道之前. 它根据支付能力将不同的支付渠道封装 ...
- 电商平台 高并发 微服务 方案_Java生鲜电商平台-秒杀系统微服务架构设计与源码解析实战...
Java生鲜电商平台- 什么是秒杀 通俗一点讲就是网络商家为促销等目的组织的网上限时抢购活动 比如说京东秒杀,就是一种定时定量秒杀,在规定的时间内,无论商品是否秒杀完毕,该场次的秒杀活动都会结束.这种 ...
最新文章
- Zabbix中文乱码问题
- ITK:将网格转换为非结构化网格
- nginx反向代理和rewrite进行解决跨域问题、去掉url中的一部分字符串,通过nginx正则生成新的url
- .NET Core 3.0 webapi集成Swagger 5.0
- Unix 多进程编程
- WIN7开启WIFI
- 技术小故事-Activity的Launch Mode引起的动画“疑案”
- ubuntu不能更新包
- 为什么我电脑的所有浏览器都开不了网页
- Jupyter Notebook从入门到精通
- python二次开发ug_CAD二次开发(UG/Proe/其他) - 随笔分类 - 白途思 - 博客园
- IDEA导入一个项目
- win10系统电脑修改注册表设置自动锁屏时间
- 公安部网络安全保卫局郭启全总工:《从实战出发,落实重要措施,保卫国家关键信息基础设施和大数据安全》
- 所谓的飞扬档案管理软件
- 固定的信念?(29)
- 数据仓库(六)---分布式SQL查询引擎---presto介绍
- 基本逻辑思维(命题,语言,定律)
- task定时任务不能自动注入 问题解决办法
- 用cv2实现小图片边缘扩充
热门文章
- [Git]Enter passphrase 密码不对的解决办法
- 从彩信手机报定制看中国移动运营架构的滞后
- 差分曼彻斯特编码详解
- 责任大,权力小,为什么还要当项目经理
- 写给前端应届生的职业规划建议
- git pull 报Your branch and 'origin/master' have diverged
- 全球及中国变压器行业研究及十四五规划分析报告(2022)
- PageHelper:简述对物理分页插件PageHelper的认识
- 近期媒体邀约活动总结,注意事项
- c语言中shmget相关函数,共享内存函数(shmget、shmat、shmdt、shmctl)及其范例 - guoping16的专栏 - 博客频道 - CSDN...