Home  >  Article  >  Web Front-end  >  Detailed explanation of token problem after layui login

Detailed explanation of token problem after layui login

尚
forward
2019-12-06 17:04:574605browse

Detailed explanation of token problem after layui login

layui is a very simple and practical background management system construction framework. The plug-ins inside are rich and easy to use. It only needs to be modified on the original basis. However, it is slightly less efficient in data processing. It is weak. The built-in jquery is slightly insufficient in the actual process. It would be better if the built-in mvc mode framework can be added.

Let’s first introduce the use of layui in the login area.

Login The problem is mainly in the storage call of token. First, post the code of creating token and interceptor in the background.

First introduce the jar package

<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 uses io.jsonwebtoken, and you can customize the secret key , and store login information

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;
    }

}

Decryption mainly uses the parseJWT method

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 is the decrypted token class, which stores all the information in the token

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

The created token will be placed in the request header on the page. The background uses an interceptor to determine whether it has expired. If it expires, the request will be intercepted. If successful, a new token will be returned in the response header to update the expiration time

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();
            }
        }
    }
}

After talking about tokens, let’s talk about how layui stores tokens and adds tokens to the request header every time it is rendered

form.on(&#39;submit(LAY-user-login-submit)&#39;, function (obj) {
            //请求登入接口
            admin.req({
                //实际使用请改成服务端真实接口
                url: &#39;/userInfo/login&#39;,
                method: &#39;POST&#39;,
                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: &#39;15px&#39;,
                            icon: 1,
                            time: 1000
                        }, function () {
                            location.href ="index"

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

We store the returned token information in the table stored locally in layui, in config.js The table name will be configured. Generally, layui.setter.tableName can be used directly.

Since layui's table is rendered through js, we cannot set the request header for it in js, and each table must The configuration is extremely troublesome, but the data table of layui is based on ajax request, so we choose to manually modify table.js in the module of layui so that each request will automatically carry the request header

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)[&#39;token&#39;]},
                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(&#39;<div class="&#39; + f + &#39;">&#39; + (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(&#39;<div class="&#39; + f + &#39;">数据接口请求异常:&#39; + 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)[&#39;token&#39;]:xhr.getResponseHeader("token")
                    })
                }
            })

in the table. Find this code in js, according to the above configuration

headers: {"token":layui.data(layui.setter.tableName)['token']}, here is the token to set the request header, take Go to layui.data(layui.setter.tableName)['token'] stored in the table after successful login. In this way, it is very simple to carry the token

At the same time, we need to update the expiration time of the token, so we need to Get the new token and put it in the table

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

Use the complete method of ajax to get the token and overwrite the old token of the table. If it is empty, it will not be overwritten.

After finishing the table, Let’s take a look at the request. jquery is built into layui. You can use var $ = layui, jquery to use the built-in ajax. Then we also need to configure 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)[&#39;token&#39;]},
            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}
        },

The same thing you quoted in l Find ajaxSettings in ayui.js or layui.all.js: configure it.

For more layui knowledge, please pay attention to the layui usage tutorial column.

The above is the detailed content of Detailed explanation of token problem after layui login. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:cnblogs.com. If there is any infringement, please contact admin@php.cn delete