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

本文详细介绍了Java后端在生鲜电商平台中生成Token的架构与设计,旨在防止客户端重复提交数据。选用JWT作为技术方案,通过生成和校验Token确保数据安全性。核心代码包括Token工具类和通用工具类,用于创建、验证和管理Token。此外,还展示了JWT的Java实现示例。
摘要由CSDN通过智能技术生成

Java生鲜电商平台-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的標誌,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.倪志耿

* @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 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;

}

}

io.jsonwebtoken

jjwt

0.7.0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值