layui登录后token问题详解

layui是一个非常简单且实用的后台管理系统搭建框架,里面的插件丰富使用简单,只需要在原有基础上进行修改即可,但是在数据处理方面略显薄弱,内置的jquery在实际过程中略显不足,若是能添加内置的mvc模式框架那就更好了

先介绍layui在登录这一块的使用,

登录问题主要是在token的存储调用上,先贴出后台的创建token以及拦截器的代码

首先引入jar包

<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>jackson-databind</artifactId>
                    <groupId>com.fasterxml.jackson.core</groupId>
                </exclusion>
            </exclusions>
        </dependency>

token使用io.jsonwebtoken ,可以自定义秘钥,并存储登录信息

package com.zeus.utils;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.zeus.constant.CommonConstants;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.spec.SecretKeySpec;

import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;

public class TokenUtil {
    private static Logger LOG = LoggerFactory.getLogger(TokenUtil.class);

    /**
     * 创建TOKEN
     *
     * @param id, issuer, subject, ttlMillis
     * @return java.lang.String
     * @methodName createJWT
     * @author fusheng
     * @date 2019/1/10
     */
    public static String createJWT(String id, String issuer, String subject, long ttlMillis) {

        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary("englishlearningwebsite");
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

        JwtBuilder builder = Jwts.builder().setId(id)
                .setIssuedAt(now)
                .setSubject(subject)
                .setIssuer(issuer)
                .signWith(signatureAlgorithm, signingKey);

        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp);
        }
        return builder.compact();
    }

    /**
     * 解密TOKEN
     *
     * @param jwt
     * @return io.jsonwebtoken.Claims
     * @methodName parseJWT
     * @author fusheng
     * @date 2019/1/10
     */
    public static Claims parseJWT(String jwt) {
        Claims claims = Jwts.parser()
                .setSigningKey(DatatypeConverter.parseBase64Binary("englishlearningwebsite"))
                .parseClaimsJws(jwt).getBody();
        return claims;
    }

}

解密主要使用到 parseJWT 方法

public static Contact getContact(String token) {
        Claims claims = null;
        Contact contact = null;
        if (token != null) {
         //得到claims类
            claims = TokenUtil.parseJWT(token);
            cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(claims.getSubject());
            contact = jsonObject.get("user", Contact.class);
        }
        return contact;
    }

claims 中是解密后的token类,存储token中的全部信息

//解密token          
claims = TokenUtil.parseJWT(token);        //得到用户的类型
    String issuer = claims.getIssuer();        //得到登录的时间
    Date issuedAt = claims.getIssuedAt();         //得到设置的登录id
    
    //claims.getExpiration().getTime() > DateUtil.date().getTime() ,判断tokern是否过期
    String id = claims.getId();        
    
    //得到存入token的对象          
    cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(claims.getSubject());        
    Contact  contact = jsonObject.get("user", Contact.class);

创建好的token会在页面中放置到请求头中,后台通过来拦截器来判断是否过期,若过期则拦截请求,成功则在响应头中返回新的token更新过期时间

package com.zeus.interceptor;


import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.zeus.utils.TokenUtil;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;

import static com.zeus.constant.CommonConstants.EFFECTIVE_TIME;

/**
 * 登陆拦截器
 *
 * @author:fusheng
 * @date:2019/1/10
 * @ver:1.0
 **/
public class LoginHandlerIntercepter implements HandlerInterceptor {
    private static final Logger LOG = LoggerFactory.getLogger(LoginHandlerIntercepter.class);

    /**
     * token 校验
     *
     * @param httpServletRequest, httpServletResponse, o
     * @return boolean
     * @methodName preHandle
     * @author fusheng
     * @date 2019/1/3 0003
     */
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse
     httpServletResponse, Object o) throws Exception {
        Map<String, String[]> mapIn = httpServletRequest.getParameterMap();
        JSON jsonObject = JSONUtil.parseObj(mapIn);
        StringBuffer stringBuffer = httpServletRequest.getRequestURL();

        LOG.info("httpServletRequest ,路径:" + stringBuffer + ",入参:" + JSONUtil.toJsonStr(jsonObject));

        //校验APP的登陆状态,如果token 没有过期
        LOG.info("come in preHandle");
        String oldToken = httpServletRequest.getHeader("token");
        LOG.info("token:" + oldToken);
        /*刷新token,有效期延长至一个月*/
        if (StringUtils.isNotBlank(oldToken)) {
            Claims claims = null;
            try {
                claims = TokenUtil.parseJWT(oldToken);
            } catch (Exception e) {
                e.printStackTrace();
                String str = "{\"code\":801,\"msg\":\"登陆失效,请重新登录\"}";
                dealErrorReturn(httpServletRequest, httpServletResponse, str);
                return false;
            }
            if (claims.getExpiration().getTime() > DateUtil.date().getTime()) {
                String userId = claims.getId();
                try {
                    String newToken = TokenUtil.createJWT(claims.getId(), claims.getIssuer(), 
                    claims.getSubject(), EFFECTIVE_TIME);
                    
                    LOG.info("new TOKEN:{}", newToken);
                    httpServletRequest.setAttribute("userId", userId);
                    httpServletResponse.setHeader("token", newToken);
                    LOG.info("flush token success ,{}", oldToken);
                    return true;
                } catch (Exception e) {
                    e.printStackTrace();
                    String str = "{\"code\":801,\"msg\":\"登陆失效,请重新登录\"}";
                    dealErrorReturn(httpServletRequest, httpServletResponse, str);
                    return false;
                }
            }
        }
        String str = "{\"code\":801,\"msg\":\"登陆失效,请重新登录\"}";
        dealErrorReturn(httpServletRequest, httpServletResponse, str);
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, 
    Object o, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse
     httpServletResponse, Object o, Exception e) throws Exception {
    }

    /**
     * 返回错误信息给WEB
     *
     * @param httpServletRequest, httpServletResponse, obj
     * @return void
     * @methodName dealErrorReturn
     * @author fusheng
     * @date 2019/1/3 0003
     */
    public void dealErrorReturn(HttpServletRequest httpServletRequest, HttpServletResponse
     httpServletResponse, Object obj) {
        String json = (String) obj;
        PrintWriter writer = null;
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("application/json; charset=utf-8");
        try {
            writer = httpServletResponse.getWriter();
            writer.print(json);

        } catch (IOException ex) {
            LOG.error("response error", ex);
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }
}

讲完了token ,再讲layui如何存储token,并在每次渲染时添加token到请求头中

form.on('submit(LAY-user-login-submit)', function (obj) {
            //请求登入接口
            admin.req({
                //实际使用请改成服务端真实接口
                url: '/userInfo/login',
                method: 'POST',
                data: obj.field,
                done: function (res) {
                    if (res.code === 0) {
                        //请求成功后,写入 access_token
                        layui.data(setter.tableName, {
                            key: "token",
                            value: res.data.token
                        });
                        //登入成功的提示与跳转
                        layer.msg(res.msg, {
                            offset: '15px',
                            icon: 1,
                            time: 1000
                        }, function () {
                            location.href ="index"

                        });
                    } else {
                        layer.msg(res.msg, {
                            offset: '15px',
                            icon: 1,
                            time: 1000
                        });
                    }
                }
            });
        });

我们将返回的token信息存入layui本地存储的表中,在config.js中会配置表名,一般直接使用layui.setter.tableName 即可,

由于layui的table 是通过js渲染的,我们无法在js中对它进行设置请求头,而且每一个表格都要配置极为麻烦,但layui的数据表格是基于ajax请求的,所以我们选在在layui的module中手动修改table.js使得,每次请求是都会自动携带请求头

a.contentType && 0 == a.contentType.indexOf("application/json") && (d = JSON.stringify(d)), t.ajax({
    type: a.method || "get",
    url: a.url,
    contentType: a.contentType,
    data: d,
    dataType: "json",
    headers: {"token":layui.data(layui.setter.tableName)['token']},
    success: function (t) {
        if(t.code==801){
            top.location.href = "index";
        }else {
            "function" == typeof a.parseData && (t = a.parseData(t) || t), 
            t[n.statusName] != n.statusCode ? (i.renderForm(), i.layMain.html('<div class="'
             + f + '">' + (t[n.msgName] || "返回的数据不符合规范,正确的成功状态码 ("
              + n.statusName + ") 应为:" + n.statusCode) + "</div>"))
               : (i.renderData(t, e, t[n.countName]), o(), a.time = (new Date).getTime()
                - i.startTime + " ms"), i.setColsWidth(), "function" 
                == typeof a.done && a.done(t, e, t[n.countName])
        }
    },
    error: function (e, t) {
        i.layMain.html('<div class="' + f + '">数据接口请求异常:' + t + "</div>"),
         i.renderForm(), i.setColsWidth()
    },
    complete: function( xhr,data ){
        layui.data(layui.setter.tableName, {
            key: "token",
            value: xhr.getResponseHeader("token")==null?layui.data(layui.setter.tableName)['token']:
                xhr.getResponseHeader("token")
        })
    }
})

在table.js中找到这一代码,按上面的配置

headers: {"token":layui.data(layui.setter.tableName)['token']},这里是设置请求头的token,拿到登录成功后存储在表中的layui.data(layui.setter.tableName)['token'], 这样既可携带token很简单

同时我们需要更新token的过期时间,那么就要拿到新的token,并放入表中

complete: function( xhr,data ){
layui.data(layui.setter.tableName, {
key: "token",
value: xhr.getResponseHeader("token")==null?layui.data(layui.setter.tableName)['token']:
xhr.getResponseHeader("token") 
})
}

使用ajax的complete方法拿到token,并覆盖表的旧token,如果为空则不覆盖

table讲完,来看看请求,layui中内置了jquery,可以使用var $ = layui,jquery, 来使用内置的ajax,那么我们也需要对ajax进行配置

pe.extend( {
active: 0, lastModified: {}
, etag: {}
, ajaxSettings: {
	url: en, type: "GET", isLocal: Vt.test(tn[1]), global: !0, processData: !0, async: !0, headers: {
		"token": layui.data(layui.setter.tableName)['token']
	}
	, contentType: "application/x-www-form-urlencoded; charset=UTF-8", accepts: {
		"*": Zt, text: "text/plain", html: "text/html", xml: "application/xml,
		   text/xml", json: "application/json, text/javascript"
	}
	, contents: {
		xml: /\bxml\b/, html: /\bhtml/, json: /\bjson\b/
	}
	, responseFields: {
		xml: "responseXML", text: "responseText", json: "responseJSON"
	}
	, converters: {
		"* text": String, "text html": !0, "text json": pe.parseJSON, "text xml": pe.parseXML
	}
	, flatOptions: {
		url: !0, context: !0
	}
},

同样在l你引用的ayui.js或者layui.all.js中找到 ajaxSettings:配置一下即可。


推荐文章
layui如何自定义字体图标?(图文介绍)

layui的图标取自于阿里巴巴的矢量图标库Iconfont第一步,通过浏览器打开http://iconfont.cn/,访问阿里巴巴矢量图标库。第二步,通过右上角的用户图标,授权登陆一个账号,在此我选

layui如何获取table某一行的值?

layui中可以使用table.checkStatus('ID')方法获取table某一行的值,其中ID为基础参数。【自动化渲染】……【方法渲染】table.render({//其它参数省略 id:'

layui怎么隐藏表格行?

layui数据表格隐藏列在使用layui做数据表格的时候,插入的数据的id是不是一组连续的数字,那么就需要用到layui的cols的type属性。table.render({ elem:'#test'

layui如何支持 es5 写法?方法介绍

写js的时候会写一些es5的代码,但是打包到dist时,发现不支持语法,怎么办呢。两步添加es5支持:一、使用npm下载安装babel,进入gulpfile.js所在目录。$npminstall--s

浅谈layui数据表格隐藏列的方法

在使用layui做数据表格的时候,插入的数据的id是不是一组连续的数字,那么就需要用到layui的cols的type属性。table.render({ elem:'#test' ,url:'${pag

浅谈layui文件上传、预览及修改

单文件上传1、HTML &#xe67c;上传客服二维码 * 开始上传 温馨提示:每次最多上传一张图片,单张图片的大小不超过2MB 2、js部分varform=layui.for

layui中怎么设置select下拉框自动选中某项?

1、需求场景分析用户需要更改某一项活动的信息,活动信息中包含需要用下拉框选择的字段A。在用户点击进行修改弹出对话框的瞬间需要填补已有的活动信息,自动选择字段A已经存在的值。2、Layui的select

layui是什么软件?

layui不是软件,它是一款采用自身模块规范编写的前端UI框架,遵循原生HTML/CSS/JS的书写与组织形式,门槛极低,拿来即用。其外在极简,却又不失饱满的内在,体积轻盈,组件丰盈,从核心代码到AP

layui中如何获取表格全部数据?

layui中获取表格全部数据第一步可以通过layui.table.cache["testReload"]的方式来获取表格全部数据,其中testReload是表格id第二步打开网页,我们可以看一下要获取

layui中关于弹出层关闭和刷新问题

描述:主页面,弹出第一个弹框,第一个弹框中在弹出第二个弹框,如图:1是主页面,2是子弹窗,3是孙弹窗功能一:好了,第一个我要实现的功能是点击弹窗3的确定之后关闭弹窗3上js代码,先贴出如何在主页面1中

laypage组件常见用法总结

laypage的使用非常简单,指向一个用于存放分页的容器,通过服务端得到一些初始值,即可完成分页渲染。核心方法:laypage.render(options)来设置基础参数。一、laypage的常用基

浅谈layui中table的sort排序

今天来谈谈tablesort的那点事。预告一下,目的是做到前台排序还有服务器排序区分开,目前的table的分页实际是比较“乱”的,相信很多用的有一段时间的特别是做server排序的同学都会多多少少遇到

layui input只读属性是什么?

layuiinput只读属性是什么?layuiinput只读属性是readonly。readonly属性规定输入字段为只读。只读字段是不能修改的。不过,用户仍然可以使用tab键切换到该字段,还可以选中

layui兼不兼容ie8?

layui兼不兼容ie8?layui兼容ie8,layui兼容人类正在使用的全部浏览器(IE6/7除外),可作为PC端后台系统与前台界面的速成开发方案。layui(谐音:类UI)是一款采用自身模块规范

layui和layer的区别是什么?

layui是之前layerlaydate等独立组件的超集合;layui是一个完整的前端框架,和Bootstrap是同一类事务;引入了layui之后layer就不用单独引入了。layer至今仍作为lay

浅谈layui标签输入框inputTags

layui标签输入框inputTags样式:目录结构:页面代码: inputTags layui.config({ base:'js/

layui框架之如何导出表格全部数据?

2020.1.16最近终于又用到了导出表格,我在实际项目里使用了上次发现的导出全部数据方法,然后遇到了一些问题,并进行了解决,终于可以正式写在博客中了。整体代码: //导出表格配置 table.r

layui框架属于前端还是后端?

layui框架属于前端还是后端?layui框架属于前端,是前端框架。layui是一款采用自身模块规范编写的前端UI框架,遵循原生HTML/CSS/JS的书写与组织形式,门槛极低,拿来即用。其外在极简,

layui动态表格之合并单元格

需求:下面用excel表格大概模拟下需求,左边是原来的,要改成右边这样的:①第一步:再生成表格后调用此方法,以合并重复的单元格done:function(res,curr,count){ merge(

layui是基于vue的吗?

layui是一款采用自身模块规范编写的前端UI框架,遵循原生HTML/CSS/JS的书写与组织形式,门槛极低,拿来即用。其外在极简,却又不失饱满的内在,体积轻盈,组件丰盈,从核心代码到API的每一处细