精华内容
下载资源
问答
  • 后台不用保存token,只需要验证是否是自己签发的token 支持多种前段,如移动端和浏览器 缺点和解决方法 每次都要去数据库查询权限信息验证token 解决: 将查询到的权限数据保存到session中,之后可以直接从session...

    @TOC

    1、基本概念

    为什么要使用token

    优点

    1. 后台不用保存token,只需要验证是否是自己签发的token
    2. 支持多种前段,如移动端和浏览器

    缺点和解决方法

    每次都要去数据库查询权限信息验证token
    解决:
    将查询到的权限数据保存到session中,之后可以直接从session中获取
    也可以使用redisJ解决

    2、代码实操

    tokenVo

    过期时间:方便前端人员判断是否置换,在快过期但用户又有新操作时需要置换

    package com.bean.vo;
    import java.io.Serializable;
    /**
     * 返回前端-Token相关VO
     */
    public class TokenVO implements Serializable {
    
    	/** 用户认证凭据 */
    	private String token;
    	/** 过期时间 */
    	//通常是总毫秒数: token生成时间+有效时长
    	private long expTime;
    	/** 生成时间 */
    	//通常是总毫秒数
    	private long genTime;
    
    	public String getToken() {
    		return token;
    	}
    	public void setToken(String token) {
    		this.token = token;
    	}
    	public long getExpTime() {
    		return expTime;
    	}
    	public void setExpTime(long expTime) {
    		this.expTime = expTime;
    	}
    	public long getGenTime() {
    		return genTime;
    	}
    	public void setGenTime(long genTime) {
    		this.genTime = genTime;
    	}
    	
    	public TokenVO() {
    		super();
    	}
    	public TokenVO(String token, long expTime, long genTime) {
    		super();
    		this.token = token;
    		this.expTime = expTime;
    		this.genTime = genTime;
    	}
    }
    

    2.1、前端

    localStorage(可以将token保存到这里)
    res.data是dto的data属性

     <script type="text/javascript">
    	  $(".submit").bind("click", function () {
    		  $.post(
    		  		"/auth/login",
    				$("#actionForm").serialize(),
    			    function (res) {
    					//判断
    					if (res.success == "true"){ //登录成功
    						//将token保存到localStorage
    						localStorage.token = res.data.token;
    						//保存token过期时间
    						localStorage.tokenExpire = res.data.expTime;
    						//跳转页面
    						location = "/page/main.html";
    					}else {
    						//显示错误提示
    						$(".info").html(res.msg);
    					}
    				}
    		  );
    	  })
      </script>
    
    package com.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class ViewController {
    
        @RequestMapping("/")
        public String base(){
            System.out.println(">>> base");
            return "/page/login.html";
        }
    }
    
    

    TokenUtil.java

    session时自动刷新,而token要前端来请求,所以token的置换剩余时间少于1个小时
    UserAgentInfo类来自于辅助工具依赖 的插件

    package com.util;
    
    import com.alibaba.fastjson.JSON;
    import cz.mallat.uasparser.UserAgentInfo;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Arrays;
    import java.util.Calendar;
    import java.util.Date;
    
    @Component
    public class TokenUtil {
    
        @Autowired
        private RedisUtil redisUtil;
    
        private int session_timeout = 60 * 60 * 2; //token有效时长
    
        private int replacement_protection_timeout = 60 * 60; //token置换保护时长
    
        private int replacement_delay= 60 * 2; //旧token延迟过期时间
    
        private String token_prefix = "token:";//token前缀
    
        /** 获取token默认有效时长 */
        public int getTimeout(){
            return session_timeout;
        }
    
        /** 获取登录时间的总毫秒数 */
        public long getLogin(String token) throws ParseException {
            //拆分token
            String[] arr = token.split("[-]");
    
            return new SimpleDateFormat("yyyyMMddHHmmss").parse(arr[3]).getTime();
        }
    
        /** token是否存在 */
        public boolean exists(String token){
            return redisUtil.exists(token);
        }
    
        /**
         * 保存token
         * @param token
         * @param user
         */
        public void save(String token, Object user) {
            //判断, token是否以"token:PC-"开头
            if (token.startsWith(token_prefix + "PC-")){
                //PC端登录有效期2小时
                redisUtil.setString(token, JSON.toJSONString(user), session_timeout);
            }else {
                //移动端登录永久有效
                redisUtil.setString(token, JSON.toJSONString(user));
            }
        }
    
        /**
         * 读取token中的对象
         * @param token
         * @param clazz
         * @return
         */
        public Object load(String token, Class clazz){
            return JSON.parseObject(redisUtil.getString(token), clazz);
        }
    
        /**
         * 删除token
         * @param token
         */
        public void delete(String token) throws Exception {
            //判断, token在redis中是否存在
            if (!redisUtil.exists(token))
                throw new Exception();
            //删除token
            redisUtil.delete(token);
        }
    
        /**
         * 生成token
         * @param name 用户账号
         * @param id 用户id
         * @param agent 设备信息
         * @return token<br>
         *     格式:<br>
         *         PC端: 前缀PC-32位的加密name-id-yyyyMMddHHmmss-6位加密的agent<br>
         *         移动端: 前缀MOBLIE-32位的加密name-id-yyyyMMddHHmmss-6位加密的agent
         */
        public String generateToken(String name, Object id, String agent){
    
            StringBuilder sb = new StringBuilder();
    
            //添加token前缀
            sb.append(token_prefix);
    
            //添加设备类型
            try {
                //获取终端类型
                String deviceType = getDeviceType(agent);
                //添加到token
                sb.append(deviceType + "-");
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
    
            //添加账号(32位加密)
            sb.append(MD5.getMd5(name, 32) + "-");
    
            //添加id
            sb.append(id + "-");
    
            //添加登录时间, 格式yyyyMMddHHmmss
            sb.append(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "-");
    
            //添加设备信息(6加密)
            sb.append(MD5.getMd5(agent, 6));
    
            return sb.toString();
        }
    
        /**
         * 验证token
         * @param token
         * @param agent 设备信息
         * @param type 验证类型<br>
         *     1: 基本验证. 包含结果1,11,12,13,20
         *     2: 置换验证. 包含结果1,11,12,14
         * @return 结果数值<br>
         *     11: token不存在<br>
         *     12: token格式有误<br>
         *     13: token已超时<br>
         *     14: token仍在保护期<br>
         *     20: 其它token异常<br>
         *     1: token正常
         */
        public int validate(String token, String agent, String type){
            // token不存在
            if (!exists(token)) return 11;
            //拆分token字符串
            String[] tokenDetails = token.split("-");
            //获取token生成时间的总毫秒数
            long genTime = 0;
            try {
               genTime = getLogin(token);
            } catch (ParseException e) {
                e.printStackTrace();
                return 12;
            }
            //计算登录时长
            long passed = Calendar.getInstance().getTimeInMillis() - genTime;
            System.out.println(passed);
            System.out.println(passed / 1000 / 60);
            //判断, 验证类型
            if (type.equals("1")){
                //判断, 是否已超时
                if (passed > session_timeout * 1000) return 13;
                //获取登录设备加密信息
                String agentMD5 = tokenDetails[4];
                //判断, 当前设备是否与登录设备一致
                if(MD5.getMd5(agent, 6).equals(agentMD5)) return 1;
            }else {
                //判断, 是否处于置换保护期内
                if (passed < replacement_protection_timeout * 1000) return 14;
                return 1;
            }
    
            return 20;
        }
    
        /**
         * 置换token
         * @param token
         * @param agent 设备信息
         * @param clazz 置换对象的类型
         * @param nameFieldName 账号属性名
         * @param idFiledName id属性名
         * @return
         */
        public String replaceToken(String token, String agent, Class clazz, String nameFieldName, String idFiledName) {
            //获取旧token中存储的用户数据
            Object user = load(token, clazz);
            System.out.println(user);
    
            //获取旧token有效期(剩余秒数 )
            long ttl = redisUtil.getExpire(token);
    //        System.out.println("ttl:" + ttl);
            //判断token是否仍在有效期内容
    //        if (ttl > 0 || ttl == -1) {
                Object name = ""; //用户账号
                Object id = ""; //用户id
                try {
                    //获取用户账号
                    Method getName = clazz.getDeclaredMethod(nameFieldName);
                    name = getName.invoke(user);
                    //获取用户id
                    Method getId = clazz.getDeclaredMethod(idFiledName);
                    id = getId.invoke(user);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
                //生成新token
                String newToken = this.generateToken(name.toString(), id.toString(), agent);
                //保存新token
                this.save(newToken, user);
                //设置2分钟后旧token过期,注意手机端由永久有效变为2分钟后失效
                redisUtil.setString(token, JSON.toJSONString(user), replacement_delay);
    
                return newToken;
    //        }
    
    //        return null;
        }
    
        /**
         * 获取设备类型
         * @param agent
         * @return
         */
        public String getDeviceType(String agent) throws IOException {
            //用户设备信息对象
            UserAgentInfo userAgentInfo = UserAgentUtil.getUasParser().parse(agent);
            //判断, 设备类型是否未知
            if (userAgentInfo.getDeviceType().equals(UserAgentInfo.UNKNOWN)) {
                //判断, 已知设备类型是否为移动端
                if (UserAgentUtil.CheckAgent(agent)) return "MOBILE";
                //已知设备不是移动端, 则为PC端
                else return "PC";
            }
            //判断, 设备类型是否为PC端
            else if (userAgentInfo.getDeviceType().equals("Personal computer")) return "PC";
            //其他类型均识别为移动端
            else return "MOBILE";
        }
    }
    
    

    控制类

    登录方法

     @PostMapping("/login")
        @ResponseBody
        public Dto login(User user, HttpServletRequest request){
            System.out.println(">>> 用户登录");
            System.out.println(user);
    
            /* 数据验证(略) */
    
            /* 登录查询 */
            List<User> userList = userService.find(user);
    
            /* 处理查询结果 */
            //判断, 集合大小
            if (userList.size() != 1)
                return new Dto("用户名或密码错误!", "100001");
    
            //获取登录用户对象
            user = userList.get(0);
    
            //判断, 账号状态
            if (user.getStatus() == 0)
                return new Dto("账号未激活, 请先激活账号!", "100002");
    
            //登录成功,就要生成token
            String token = tokenUtil.generateToken(user.getUserName(), user.getId(),
                    request.getHeader("user-agent"));
    
            //保存token
            tokenUtil.save(token, user);
    
            try {
                //获取tokenVo
                TokenVO tokenVO = new TokenVO(token,
                        tokenUtil.getLogin(token) + tokenUtil.getTimeout()*1000,
                        tokenUtil.getLogin(token));
    
                return new Dto(tokenVO);
    
            } catch (ParseException e) {
                e.printStackTrace();
                return new Dto("token格式有误!", "100003");
            }
    
        }
    
        @PostMapping("/registe/mail")
        @ResponseBody
        public Dto registeByEmail(User user){
            System.out.println(">>> 用户注册--邮箱");
            System.out.println(user);
    
            //添加用户
            userService.addUserByMail(user);
    
            return new Dto("注册成功, 请尽快激活账号!");
        }
    

    启动启动类
    启动nginx和redis
    就可以访问了

    获取header中的token

    有时候我们登录之后要去token中拿数据
    在这里插入图片描述

     @GetMapping("/login/info")
        @ResponseBody
        public Dto getLoginInfo(HttpServletRequest request){
            System.out.println(">>> 获取登录用户信息");
    
            //获取header中的token
            String token = request.getHeader("token");
            System.out.println(token);
    
            //验证token是否正常
            int result = tokenUtil.validate(token, request.getHeader("user-agent"), "1");
    
            //根据验证结果的所有异常
            switch (result){
                case 11: 
                    return new Dto("token不存在", "200011");
                case 12:
                    return new Dto("token格式有误", "200012");
                case 13:
                    return new Dto("token已超时", "200013");
                case 20:
                    return new Dto("token异常", "200020");
            }
    
            //获取token中的对象
            User user = (User) tokenUtil.load(token, User.class);
    
            //返回
            return new Dto(user);
        }
    

    前端
    每次需要登录向后台请求都要带上 headers

    	  /* 加载登录用户信息 */
    	  $.ajax({
    	     type: "get",
    		 url: "/auth/login/info",
    		 success: function (res) {
    			 if (res.success == "true") {
    				 $("#loginUserName").html(res.data.realName);
    			 }else {
    				 $("#loginUserName").html("游客(未登录)");
    			 }
    		 },
    	     headers: {
    		 	token: localStorage.token
    		 }
    	  });
    

    置换token,退出

    置换

    前端需要判断token是否过期,如果过期就需要置换(如果需要登录才能访问的情况下)

    前端

    	 function parseToken(token){
    		  //拆分token
    		  var arr = token.split("-");
    		  //获取年
    		  var year = arr[3].substring(0, 4);
    		  //获取月
    		  var month = arr[3].substring(4, 6);
    		  //获取日
    		  var date = arr[3].substring(6, 8);
    		  //获取时
    		  var hours = arr[3].substring(8, 10);
    		  //获取分
    		  var min = arr[3].substring(10, 12);
    		  //获取秒
    		  var sec = arr[3].substring(12);
    
    		  return new Date(year, month-1, date, hours, min, sec);
    		  // alert(d);
    	  }
    
    	  replaceToken();
    
    	  //置换token
    	  function replaceToken() {
    		  //获取token
    		  var token = localStorage.token;
    		  //获取过期时间
    		  var expTime = localStorage.tokenExpire;
    		  // alert(expTime)
    		  // var date = parseToken(token);
    
    		  //获取剩余登录时间的毫秒数
    		  var loginTimeLeft =  expTime - new Date().getTime();
    		  // alert(loginTimeLeft < 1000 * 60 * 60);
    		  //判断, 是否需要置换token
    		  if (loginTimeLeft < 1000 * 60 * 60){
    		  	$.ajax({
    				headers: {
    					token: localStorage.token
    				},
    				type: "get",
    				url: "/auth/token/replace",
    				success: function (res) {
    					if (res.success == "true"){
    						localStorage.token = res.data.token;
    						localStorage.tokenExpire = res.data.expTime;
    					}
    					// else{
    					// 	alert(res.msg);
    					// }
    				}
    			});
    		  }
    	  }
    
    

    控制类

     @GetMapping("/token/replace")
        @ResponseBody
        public Dto replaceToken(HttpServletRequest request){
            System.out.println(">>> 置换token");
    
            //获取header中的token
            String token = request.getHeader("token");
            System.out.println(token);
    
            //验证token是否正常
            int result = tokenUtil.validate(token, null,"2");
    
            try {
    //            System.out.println(tokenUtil.getLogin(token));
                System.out.println(new Date(tokenUtil.getLogin(token)));
                System.out.println(tokenUtil.getTimeout());
            } catch (ParseException e) {
                e.printStackTrace();
            }
    
            //根据验证结果的所有异常
            switch (result){
                case 11:
                    return new Dto("token不存在", "200011");
                case 12:
                    return new Dto("token格式有误", "200012");
                case 14:
                    return new Dto("token已超时", "200013");
            }
    
            //置换token, 获取新token
            token = tokenUtil.replaceToken(token, request.getHeader("user-agent"),
                    User.class, "userName", "id");
    
            //判断, 新token
            if (token == null)
                return new Dto("token置换失败!", "200030");
    
    
    
            try {
                TokenVO tokenVO = new TokenVO(token,
                        tokenUtil.getLogin(token) + tokenUtil.getTimeout()*1000,
                        tokenUtil.getLogin(token));
    
                return new Dto(tokenVO);
    
            } catch (ParseException e) {
                e.printStackTrace();
                return new Dto("token格式有误!", "100003");
            }
        }
    
        @GetMapping("/login/info")
        @ResponseBody
        public Dto getLoginInfo(HttpServletRequest request){
            System.out.println(">>> 获取登录用户信息");
    
            //获取header中的token
            String token = request.getHeader("token");
            System.out.println(token);
    
            //验证token是否正常
            int result = tokenUtil.validate(token, request.getHeader("user-agent"), "1");
    
            //根据验证结果的所有异常
            switch (result){
                case 11:
                    return new Dto("token不存在", "200011");
                case 12:
                    return new Dto("token格式有误", "200012");
                case 13:
                    return new Dto("token已超时", "200013");
                case 20:
                    return new Dto("token异常", "200020");
            }
    
            //获取token中的对象
            User user = (User) tokenUtil.load(token, User.class);
    
            //返回
            return new Dto(user);
        }
    

    退出

    验证,是否是我记录的token,以及是否是同个设备
    前端

     <a href="javascript:void(0)" id="logoff">退出</a>
     function replaceToken() {
    		
    	  $("#logoff").bind("click", function () {
    
    	  	if (!confirm("确定退出系统吗?")) return;
    
    		$.ajax({
    			headers: {
    				token: localStorage.token
    			},
    		  	url: "/auth/logoff",
    			type: "get",
    			success: function (res) {
    				if (res.success == "true") {
    				#清除
    					localStorage.token = null;
    					localStorage.tokenExpire = null;
    					location = "/page/login.html";
    				}
    			}
    		});
      })
    

    后台

     @GetMapping("/logoff")
        @ResponseBody
        public Dto logoff(HttpServletRequest request){
            System.out.println(">>> 退出登录");
    
            //获取header中的token
            String token = request.getHeader("token");
            System.out.println(token);
    
            //删除token
            try {
                tokenUtil.delete(token);
            } catch (Exception e) {
                e.printStackTrace();
                return new Dto("token不存在!", "100011");
            }
    
            return new Dto("退出成功!");
        }
    
    展开全文
  • PyJWT生成token

    2020-08-03 19:34:56
    使用Pyjwt在django中创建和认证token,用于在移动端来认证和用户,本文通过自己编写模型来实现根据用户来生成token,在请求头中添加Authentication来进行认证,保持登录状态。可以直接使用,编写过程可以查看本人...
  • 码农公社 210.net.cn210= 102410月24日一个重要的...JWT的原理JWT的原理是,服务器认证以后,生成一个JSON格式的对象,发回给客户端,就像下面这样.{ "用户名": "admin", "角色": "超级管理员", "到期时间": "2019-0...

    201610181557196870.jpg

    码农公社  210.net.cn  210= 1024  10月24日一个重要的节日--码农(程序员)节

    JWT简介

    JSON Web Token(缩写 JWT),是目前最流行的跨域认证解决方案。

    JWT的原理

    JWT的原理是,服务器认证以后,生成一个JSON格式的对象,发回给客户端,就像下面这样.

    {   "用户名": "admin",   "角色": "超级管理员",   "到期时间": "2019-07-13 00:00:00" }

    以后,客户端与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。

    为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名(详见后文)。

    服务器不再保存任何 session 数据,也就是服务器变成无状态了,从而比较容易实现扩展。

    3.JWT的数据结构

    实际的 JWT是一个很长的字符串,中间用点(.)分隔成三个部分。 就像下面这样:

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImp0aSI6IjNmMmc1N2E5MmFhIn0.eyJpYXQiOjE1NjI4MzM0MDgsImlzcyI6Imh0dHA6XC9cL3d3dy5weWcuY29tIiwiYXVkIjoiaHR0cDpcL1wvd3d3LnB5Zy5jb20iLCJuYmYiOjE1NjI4MzM0MDcsImV4cCI6MTU2MjkxOTgwOCwianRpIjoiM2YyZzU3YTkyYWEiLCJ1c2VyX2lkIjoxfQ.NFq1qQ-Z5c4pwit8ZkyWEwX6SBXmnHJcc6ZDgSD5nhU

    JWT的三个部分依次如下:

    - Header(头部) - Payload(负载) - Signature(签名)

    4.JWT的使用方式

    客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。  此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面

    5.JWT的几个特点

    (1)JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。

    (2)JWT 不加密的情况下,不能将秘密数据写入 JWT。

    (3)JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。

    (4)JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。

    (5)JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

    (6)为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

    6.JWT功能实现

    使用composer安装 JWT 功能组件

    composer require lcobucci/jwt 3.3

    namespace tools\jwt;

    use Lcobucci\JWT\Builder;

    use Lcobucci\JWT\Parser;

    use Lcobucci\JWT\Signer\Hmac\Sha256;

    use Lcobucci\JWT\ValidationData;

    /**

    * Created by PhpStorm.

    * User: asus

    * Date: 2019/4/5

    * Time: 13:02

    */

    class Token

    {

    private static $_config = [

    'audience' => '',//接收人

    'id' => '',//token的唯一标识,这里只是一个简单示例

    'sign' => '',//签名密钥

    'issuer' => '',//签发人

    'expire' => 3600*24 //有效期

    ];

    //生成token

    public static function getToken($user_id){

    //签名对象

    $signer = new Sha256();

    //获取当前时间戳

    $time = time();

    //设置签发人、接收人、唯一标识、签发时间、立即生效、过期时间、用户id、签名

    $token = (new Builder())->issuedBy(self::$_config['issuer'])

    ->canOnlyBeUsedBy(self::$_config['audience'])

    ->identifiedBy(self::$_config['id'], true)

    ->issuedAt($time)

    ->canOnlyBeUsedAfter($time-1)

    ->expiresAt($time + self::$_config['expire'])

    ->with('user_id', $user_id)

    ->sign($signer, self::$_config['sign'])

    ->getToken();

    return (string)$token;

    }

    //从请求信息中获取token令牌

    public static function getRequestToken()

    {

    if (empty($_SERVER['HTTP_AUTHORIZATION'])) {

    return false;

    }

    $header = $_SERVER['HTTP_AUTHORIZATION'];

    $method = 'bearer';

    //去除token中可能存在的bearer标识

    return trim(str_ireplace($method, '', $header));

    }

    //从token中获取用户id (包含token的校验)

    public static function getUserId($token = null)

    {

    $user_id = null;

    $token = empty($token)?self::getRequestToken():$token;

    if (!empty($token)) {

    //为了注销token 加以下if判断代码

    $delete_token = cache('delete_token') ?: [];

    if(in_array($token, $delete_token)){

    //token已被删除(注销)

    return $user_id;

    }

    $token = (new Parser())->parse((string) $token);

    //验证token

    $data = new ValidationData();

    $data->setIssuer(self::$_config['issuer']);//验证的签发人

    $data->setAudience(self::$_config['audience']);//验证的接收人

    $data->setId(self::$_config['id']);//验证token标识

    if (!$token->validate($data)) {

    //token验证失败

    return $user_id;

    }

    //验证签名

    $signer = new Sha256();

    if (!$token->verify($signer, self::$_config['sign'])) {

    //签名验证失败

    return $user_id;

    }

    //从token中获取用户id

    $user_id = $token->getClaim('user_id');

    }

    return $user_id;

    }

    }

    展开全文
  • (1)"用户登录页面.md"接口文档 (2)用户微服务工程(dm-user-provider,dm-user-consumer) (2)生成token机制原理 (3)缓存用户信息和token信息 (4)修改数据库脚本: SELECT * FROM dm_user WHERE phone= 'cj88sd...

    用户密码登录接口

    集群:多台机子同时部署一个服务

    客户端负载均衡:Ribbon

    1.物料:
    (1)"用户登录页面.md"接口文档
    (2)用户微服务工程(dm-user-provider,dm-user-consumer)
    (2)生成token机制原理
    (3)缓存用户信息和token信息
    (4)修改数据库脚本:

    SELECT * FROM dm_user WHERE phone= 'cj88sdj@163.com' AND PASSWORD = 'e10adc3949ba59abbe56e057f20f883e'
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O9UG8Dcj-1573225333163)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1573140660109.png)]

    2.实现用户密码登录接口
    思路1:使用到dm-user-provider,dm-user-consumer,dm-common
    思路2:redisUtils.get(tokenKey)方法代表的含义:判断redis中是否存在该对象

    步骤1:
    dm-common/dm-common-dao/mapper/DmUserMapper接口

    public DmUser checkLoginByPassword(@Param(value = "phone") String phone, @Param(value = "password") String password) throws Exception;

    dm-common/dm-common-dao/resources/DmUserMapper.xml

    <select id="checkLoginByPassword" resultType="cn.dm.pojo.DmUser">
    		select
    		    id as id,
    		    phone as phone,
    		    wxUserId as wxUserId,
    		    realName as realName,
    		    nickName as nickName,
    		    sex as sex,
    		    idCard as idCard,
    		    birthday as birthday,
    		    createdTime as createdTime,
    		    updatedTime as updatedTime,
    		    hobby as hobby
    		from dm_user
    		where phone=#{phone} and password=#{password}
    </select>

    步骤2:dm-common/dm-common-client/RestDmUserClient
    (1)加入:

    @RequestMapping(value = "/checkLoginByPassword", method = RequestMethod.POST)
    public DmUser checkLoginByPassword(@RequestBody DmUser dmUser) throws Exception;
    
    @RequestMapping(value = "/generateToken", method = RequestMethod.POST)
    public String generateToken(@RequestBody DmUser dmUser) throws Exception;

    (2)实现:DmUserClientFallBack客户端代码

    (3)本地构建

    步骤3:
    dm-user-provider/RestDmUserService

    @RestController
    public class RestDmUserService {
    @Autowired
    private DmUserMapper dmUserMapper;
    
    @RequestMapping(value = "/checkLoginByPassword",method = RequestMethod.POST)
    public DmUser checkLoginByPassword(@RequestBody DmUser dmUser)throws Exception{
    	return dmUserMapper.checkLoginByPassword(dmUser.getPhone(),dmUser.getPassword());
    }
    }

    dm-user-provider/RestDmTokenService

    @RestController
    public class RestDmTokenService {
        private String tokenPrefix = "token";
    
        @RequestMapping(value = "/generateToken", method = RequestMethod.POST)
        public String generateToken(@RequestBody DmUser dmUser) {
            StringBuilder sb = new StringBuilder();
            sb.append(tokenPrefix + "-");
            sb.append("PC-" + "-");
            String info = MD5.getMd5(EmptyUtils.isEmpty(dmUser.getPhone()) ? dmUser.getWxUserId() : dmUser.getPhone(), 32);
            sb.append(info + "-");
            sb.append(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "-");
            sb.append(UUID.randomUUID().toString().substring(0, 6));
            return sb.toString();
        }
    }

    步骤4:创建dm-user-consumer/DmUserController

    @RestController
    @RequestMapping("/api")
    public class DmUserController {
    
        @Autowired
        private DmLoginService dmLoginService;
    
        @RequestMapping(value = "/p/login",method = RequestMethod.POST)
        public Dto loginByPassword(@RequestBody DmUser dmUser) throws Exception {
            dmUser.setPassword(MD5.getMd5(dmUser.getPassword(),32));//32为加“盐”
            Object[] results = dmLoginService.login(dmUser);
            if (EmptyUtils.isEmpty(results)) {
                return DtoUtil.returnFail("登陆失败","0000");
            }else {
                return DtoUtil.returnSuccess("登陆成功",results);
            }
        }
    }

    步骤五:dm-user-consumer/DmLoginService

    /**
      * 账号密码登陆
      *
      * @param dmUser
      * @return
      * @throws Exception
      */
      public Object[] login(DmUser dmUser) throws Exception;

    步骤六:dm-user-consumer/DmLoginServiceImpl

    @Component
    public class DmLoginServiceImpl implements DmLoginService {
        @Autowired
        private RestDmUserClient restDmUserClient;
    
        @Autowired
        private RestDmImageClient restDmImageClient;
    
        @Autowired
        private RedisUtils redisUtils;
    
    
        @Override
        public Object[] login(DmUser user) throws Exception {
    
    
            //1.通过用户名和密码两个参数到数据库里获取用户对象,并进行校验
            DmUser dmUser = restDmUserClient.checkLoginByPassword(user);
            //2.判空校验
            if (EmptyUtils.isEmpty(dmUser)) {
                return null;
            }
            //3.定义dmUserVO,并组装
            DmUserVO dmUserVO = new DmUserVO();
    
            //4.将数据库中的用户对象复制到dmUserVO中
    
            BeanUtils.copyProperties(dmUser, dmUserVO);
    
            //5.更新用户id
            dmUserVO.setUserId(dmUser.getId());
    
    
            //6.通过用户id查询图片库对象
            List<DmImage> dmImageList = restDmImageClient.queryDmImageList(dmUser.getId(),
                    Constants.Image.ImageType.normal,
                    Constants.Image.ImageCategory.user);
    
            //7.判空校验,并更新图片id和图片URL
            if (EmptyUtils.isNotEmpty(dmImageList)) {
                dmUserVO.setImageId(dmImageList.get(0).getId());
                dmUserVO.setImgUrl(dmImageList.get(0).getImgUrl());
            }
    
            //8.调用restDmUserClient中的generateToken()方法,生成token
            String token = this.generateToken(dmUser);
    
            //9.(重要!)将token和用户对象保存在redis中
            this.saveToken(token, dmUserVO);
    
            //10.组装Vo
            TokenVO tokenVO = new TokenVO(token, Constants.Redis_Expire.SESSION_TIMEOUT, System.currentTimeMillis());
    
            //11.返回用户VO,tokenVO
            return new Object[]{dmUserVO, tokenVO};
        }
    
        private String generateToken(DmUser dmUser)throws Exception {
            return restDmUserClient.generateToken(dmUser);
        }
    
    
        /**
         * (置换token)
         *  将token和用户对象保存在redis中
         */
        private void saveToken(String token, DmUserVO dmUserVO) {
            String tokenKey = Constants.USER_TOKEN_PREFIX + dmUserVO.getUserId();
            String tokenValue = null;
    
    
            //1.判断如果redis中包含token,则删除原有的token
            if ((tokenValue = (String) redisUtils.get(tokenKey)) != null) {
                redisUtils.delete(tokenKey);
            }
    
    
            //2.重新在redis中生成key为tokenKey,value为token的值
            redisUtils.set(tokenKey, Constants.Redis_Expire.SESSION_TIMEOUT, token);
    
            //3.在redis中塞入key为token,value为用户对象的值。
            redisUtils.set(token, Constants.Redis_Expire.SESSION_TIMEOUT, JSON.toJSONString(dmUserVO));
        }
    }

    打开Redis
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bWCDTgpY-1573225333164)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1573223104221.png)]

    测试:
    http://localhost:7100/api/p/login

    设置:
    headers:Content-Type application/json

    {
    "phone":"cj88sdj@163.com",
    "password":"123456"
    }

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d2lnLVCP-1573225333165)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1573223327871.png)]

    此时,已经生成了token

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KxvQG48v-1573225333165)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1573223422648.png)]

    (userToken:)文件夹的形式保存 (token为value)

    //在redis中生成两对键值对
    U_id:token
    token:user
    //但凡redis存储池的时候,只要加个冒号,它就以文件夹的形式显示在redis当中

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M8ANLDaV-1573225333166)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1573223867077.png)]

    此处为PC端生成的token(以token为key找到对象)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lh7q2hKs-1573225333167)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1573224294298.png)]
    失效时间TTL,若超过失效时间,需要重新登录,置换token

    展开全文
  • token登录+redis缓存

    千次阅读 2019-09-03 09:41:45
    token登录 1.用户登录 /** * 用户登录 * @param user * @return */ @RequestMapping(value = "/login", method = RequestMethod.POST) @ResponseBody public Result userLogin(@RequestBo...

    token登录

    1.用户登录

        /**
         * 用户登录
         * @param user
         * @return
         */
        @RequestMapping(value = "/login", method = RequestMethod.POST)
        @ResponseBody
        public Result userLogin(@RequestBody User user, HttpServletResponse response,HttpServletRequest request) {
            log.info("用户登录[start]");
            String authToken = request.getHeader(Constants.RESPONSE_TOKEN);
            if(!StringUtils.isBlank(authToken)){
                TokenAuthBO tokenBo = tokenService.verifyToken(authToken);
                if(tokenBo.isAuthState()){
                    return new Result(Result.SUCCESS_CODE, "你已经登录,无需重复登录!");
                }
            }
    
            if (StringUtils.isBlank(user.getUserName()) || StringUtils.isBlank(user.getUserPass())) {
                log.info("无效参数");
                return new Result(Result.ERROR_CODE, "无效参数!");
            }
            int count = loginService.checkUserName(user.getUserName());
            //是否存在用户
            if (count == 0) {
                log.info("没有该用户");
                return new Result(Result.ERROR_CODE, "没有该用户!");
            }
    
            User u = loginService.userLogin(user);
            if (u == null) {
                log.info("用户登失败,密码错误");
                return new Result(Result.ERROR_CODE, "密码错误!");
            }
            //用户状态为启用
            if (u.getUserStartusing() == 1) {
                log.info("用户登失败,账号禁止使用");
                return new Result(Result.ERROR_CODE, "账号禁止使用!");
            }
            // 生成Token
            Integer userId =  u.getUserId();
            Long loginTime = System.currentTimeMillis() / 1000;
            long  changePwdTime = loginTime;
            String token = tokenService.createToken(userId, loginTime, changePwdTime);
            //设置响应token
            Cookie cookie = new Cookie(Constants.RESPONSE_TOKEN,token);
            cookie.setHttpOnly(false);
            cookie.setPath("/");
            cookie.setMaxAge(60 * 60 * 24 ); //有效期一天
            response.addCookie(cookie);
            //存入缓存
            template.opsForValue().set(token,user,1, TimeUnit.DAYS);
            log.info("用户登录[end]");
            return new Result(Result.SUCCESS_CODE, "登陆成功!",template.opsForValue().get(token));
        }
    

    2.生成token

    	/**
    	 * @author sxw 创建Token
    	 * @param userId 用户Id, loginTime登录时间, changePwdTime密码修改时间
    	 * @return TokenAuthEnum 认证result枚举
    	 **/
    	public String createToken(Integer userId, Long loginTime, Long changePwdTime) {
    		log.info("创建Token[start], userId: " + userId + ", loginTime: " + loginTime + ", changePwdTime: "
    				+ changePwdTime);
    		JSONObject tokenJson = new JSONObject();
    		tokenJson.put(TOKEN_USERID, userId);
    		tokenJson.put(TOKEN_LOGIN_TIME, loginTime);
    		tokenJson.put(TOKEN_CHANGE_PWD_TIME, changePwdTime);
    		long endTime = loginTime + 72*60*60;
    		tokenJson.put(TOKEN_END_TIME, endTime);
    		String encryptToken = DESUtils.encrypt(tokenJson.toJSONString());
    		String token = Base64.encodeBase64URLSafeString(encryptToken.getBytes());
    		log.info("创建Token[end], token: " + token);
    		return token;
    	}
    

    3.校验token

    	/**
    	 * @author sxw 验证Token
    	 * @param token 认证字符
    	 * @return TokenAuthEnum 认证result枚举
    	 **/
    	public TokenAuthBO verifyToken(String token) {
    		log.info("token认证开始[start], token: " + token);
    		if (StringUtils.isBlank(token)) {
    			log.warn("token认证开始[end], token is null~");
    			return new TokenAuthBO(TokenAuthEnum.FAIL_ISNULL, Collections.emptyMap());
    		}
    
    		JSONObject tokenJson = null;
    		try {
    			String base64Token = new String(Base64.decodeBase64(token));
    			String decrypt = DESUtils.decrypt(base64Token);
    			tokenJson = JSONObject.parseObject(decrypt);
    			log.info("token解析后的数据,tokenJson: " + tokenJson);
    			if (MapUtils.isBlank(tokenJson) || tokenJson.isEmpty()) {
    				throw new SecurityException("tokenJson is NULL");
    			}
    			tokenJson.put(TOKEN, token);
    		} catch (SecurityException | JSONException ex) {
    			log.error("token 解析解密异常,message: " + ex.getMessage());
    			return new TokenAuthBO(TokenAuthEnum.FAIL, Collections.emptyMap());
    		}
    
    		if (MapUtils.isEmptyMap(tokenJson,
    				new String[] { TOKEN_USERID, TOKEN_LOGIN_TIME, TOKEN_END_TIME, TOKEN_CHANGE_PWD_TIME })) {
    			log.warn("token缺失数据~");
    			return new TokenAuthBO(TokenAuthEnum.FAIL_DATA_ERROR, tokenJson);
    		}
    
    		if (!StringUtils.isNumeric(tokenJson.getString(TOKEN_USERID))
    				|| !StringUtils.isNumeric(tokenJson.getString(TOKEN_LOGIN_TIME))
    				|| !StringUtils.isNumeric(tokenJson.getString(TOKEN_END_TIME))
    				|| !StringUtils.isNumeric(tokenJson.getString(TOKEN_CHANGE_PWD_TIME))) {
    			log.warn("token数据格式有误~");
    			return new TokenAuthBO(TokenAuthEnum.FAIL_DATA_ERROR, tokenJson);
    		}
    
    		Integer loginTime = Integer.parseInt(tokenJson.getString(TOKEN_LOGIN_TIME));
    		Integer endTime = Integer.parseInt(tokenJson.getString(TOKEN_END_TIME));
    		Integer change_pwd_time = Integer.parseInt(tokenJson.getString(TOKEN_CHANGE_PWD_TIME));
    
    		// 判断token时间有效性
    		long thisTime = System.currentTimeMillis() / 1000;
    		if (!(thisTime >= loginTime && thisTime <= endTime)) {
    			log.warn("token timeOut, token解析后的数据,tokenJson: " + tokenJson + ", thisTime: " + thisTime);
    			return new TokenAuthBO(TokenAuthEnum.FAIL_TIMEOUT, tokenJson);
    		}
    
    		// 判断userId有效性
    		String userId = tokenJson.getString(TOKEN_USERID);
    		Map<String,Object> data = tokenDao.getObjectByUserId(userId);
    		String newChangeTime = data.get("pwdChangeTime").toString();
    		Integer num = (Integer) data.get("num");
    		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		Date date = null;
    		try {
    			date = simpleDateFormat.parse(newChangeTime);
    		} catch (ParseException e) {
    			e.printStackTrace();
    		}
    		long newChangeTimeNow =   date.getTime()/1000;
    		if (num == 0) {
    			log.warn("认证失败,非有效用户~");
    			return new TokenAuthBO(TokenAuthEnum.FAIL, tokenJson);
    		}
    
    		// 判断密码最新修改时间是否大于token中历史修改时间
    		if (newChangeTimeNow > change_pwd_time.intValue()) {
    			log.warn("认证成功,密码已过期[密码修改时间: " + newChangeTimeNow + "], token密码时间: " + change_pwd_time);
    			return new TokenAuthBO(TokenAuthEnum.FAIL_PWDCHANGE, tokenJson);
    		}
    		Object user = template.opsForValue().get(token);
    		if(user == null){
    			log.warn("redis已过期,请更新redis");
    			return new TokenAuthBO(TokenAuthEnum.FAIL_PWDCHANGE, tokenJson);
    		}
    		log.info("token认证成功 \n"+"token="+token+"\n"+"tokenInfo="+tokenJson);
    		return new TokenAuthBO(TokenAuthEnum.SUCCESS, tokenJson);
    	}
    

    4.yml配置redis

      redis:
        database: 0
        host: localhost
        jedis:
          pool:
            max-active: 8
            max-idle: 8
            max-wait: -1ms
            min-idle: 0
        lettuce:
          pool:
            max-active: 8
            max-idle: 8
            max-wait: -1ms
            min-idle: 0
          shutdown-timeout: 100ms
        password: 123456
        port: 6379
    

    5.pom.xml配置

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-json</artifactId>
            </dependency>
    

    6.redis序列化

    @Configuration
    public class RedisConfig {
    
        //还是使用springboot默认配置的RedisConnectionFactory
        @Autowired
        private RedisConnectionFactory redisConnectionFactory;
    
        // 默认用的是用JdkSerializationRedisSerializer进行序列化的
        @Bean
        @SuppressWarnings({ "rawtypes", "unchecked" })
        public RedisTemplate<String, Object> redisTemplate() {
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
            // 注入数据源
            redisTemplate.setConnectionFactory(redisConnectionFactory);
            // 使用Jackson2JsonRedisSerialize 替换默认序列化
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
    
            // key-value结构序列化数据结构
            redisTemplate.setKeySerializer(stringRedisSerializer);
            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
            // hash数据结构序列化方式,必须这样否则存hash 就是基于jdk序列化的
            redisTemplate.setHashKeySerializer(stringRedisSerializer);
            redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
            // 启用默认序列化方式
            redisTemplate.setEnableDefaultSerializer(true);
            redisTemplate.setDefaultSerializer(jackson2JsonRedisSerializer);
    
            /// redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }
    
    }
    
    展开全文
  • 用户登录返回token

    2020-08-10 14:58:51
    'rest_framework.authentication.TokenAuthentication', # token认证 ) } 同步数据库, 生成authtoken_token表 python manage.py migrate 执行完成后, 数据库就会多出一张authtoken_token表 views编辑登录的视图函数...
  • uni-app实现登录保存Token

    千次阅读 2020-12-19 16:01:54
    详情查看:Sliver Rest Wp api:全功能的WordPress api工具引言在上一小节中我向大家展示了如何在uni中判断用户是否登录,在本小节中我将利用前面的登录代码实现token的存储。开始后端打开我们的主题目录下面的login...
  • 后端登录功能实现——token技术 内容参考自以下博客: https://www.yunliyunwai.cn/blog/detail/da87ec1283706f0c0995a9c235f2eb31(过度借鉴,请勿在意~) 【详细而又具体有逻辑的内容请直接点击链接访问,笔者...
  • 上一章我们搭建好了授权认证服务器,也能成功返回token了,oauth2默认的格式如下: { "access_token": "86d17c41-4de9-470a-a637-0acb7237099d", "token_type": "bearer", "refresh_token": "fb255411-9535-47ad-...
  • 生成JWT格式token

    千次阅读 2019-01-22 11:28:21
    JWT是json web token缩写 它将用户信息加密到token里,服务器不保存任何用户信息 服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证 于token的身份验证可以替代传统的cookie+session身份验证方法 ...
  • vue 设置Header验证token登录

    千次阅读 2019-10-31 16:03:03
    3、页面应用(这里的token数据为后台返回) //设置头部token login ( ) { this . $http . post ( this . GLOBAL . serverSrc + '/api/Login/LoginOn' , { } , { header : { } , emulateJSON ...
  • 使用token验证登录信息,把生成的token存到数据库中,根据用户id判断是否重复登录,重复登录就重置到期时间 调用登录接口返回出token和uuid信息 每次访问都需要在Headers里面添加该数据,adminId对应就是uuid的...
  • Jmeter接口实战(一)之获取token登录 第一式:获取token登录 文章目录Jmeter接口实战(一)之获取token登录前言一、效果图二、token是什么1.token定义2.步骤3.与cookie对比总结 前言 本系列文章将会教你...
  • 前言:这次主要是介绍些业务逻辑,技术点...所谓落后就要挨打,那么今天就开始学习小程序的一些小知识吧(本文基于十年磨一剑的tp5)目录:微信登录换取token的流程如何将code变成openid和session_key抛出错误异常和...
  • 1、封装login_token2、headers:对应登录请求头部信息3、request_param:登录的参数数据4、json.dumps:将一个Python数据结构转换为JSON5、dict(response.json()):获取的json转换成dict格式6、return dict_token['...
  • token实现单点登录

    万次阅读 2019-03-05 15:52:40
    1,依赖架 ...nimbus-jose-jwt 4.31.1 ...java-jwt ...public class TokenJwt { /** * 秘钥 */ private static final byte[] SECRET="3d990d2276917dfac04467df11fff26d".getBytes(); /*...
  • Spring Boot 2.X 实战--Shiro (Token)登录和验证 主要介绍了 Spring Boot 整合 Spring Security 实现 Token登录和认证,这一小节中,我们将实现 Spring Boot 整合 Shiro 实现 Token登录和认证。 目录结构 ...
  • 注意:因为拦截器中使用验证用户的请求是否带有token以及携带的token是否符合最新登录用户的身份验证,所有前端调用接口的时候请求头需要把token带上。 以上步骤就完成了使用redis+token实现单点登录。 当用户退出...
  • 需要此工作流程来授权以编程方式创建的用户帐户(尚未明确授予SecureToken的用户帐户)以启用或使用FileVault并解锁APFS格式的启动卷上的磁盘加密。 学分 在 #filevault中发现并正式化了sysadminctl SecureToken...
  • vue 实现无痛刷新token 功能 // 导入 vuex 模块 import store from '@/store/index.js' // 导入路由模块 // 导入 router 模块 import router from '@/router/index.js' console.log(store) // 这是请求地址 const ...
  • 基于Token登入

    万次阅读 2018-01-07 20:27:04
    REST即表述性状态传递(英文:Representational State Transfer,简称REST)是Roy Fielding...1.基于Session登录验证(有会话状态) 用户发出登录请求,带着用户名和密码到服务器验证,服务器验证成功就将用户信息写
  • 微信小程序Token登录验证

    千次阅读 2021-02-24 23:28:48
    上图是微信开发文档提供的图。 最近开发一款小程序,看了许久的微信文档,这里来记录一下其中的...前端以后每次请求都会携带该自定义登录状态,后端进行登录状态的判断,正常就返回业务数据,否则重新登陆,获取新的.
  • JAVA 用户登录token验证

    万次阅读 2018-08-04 14:49:53
    一、登录接口实现返回token (1)controller层 //登录接口 @RequestMapping(value = "/selectUserInFo", method = RequestMethod.POST,produces = "application/json;charset=UTF-8") @ApiOpera...
  • 使用Shiro和token进行无状态登录使用Shiro和token进行无状态登录我们之前可以使用shiro实现登录,但这些都是基于session或是cookie实现的,这些只能用于单机部署的服务,或是分布式服务共享会话,显然后者开销极大,...
  • 防止输入框调用代码四、Token作用1.token作用2.token验证了什么 一、为什需要注册登录 1.注册登录的重要性 注册登录,是一个网站必备的,因为一个网站需要多个或者很多人访问,如果这些人只能看不能进行操作的话,...
  • Json web token (JWT) 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。简答理解就是一个身份凭证,用于服务识别。 JWT本身是无状态的,这点有别于传统的session,不在服务端存储凭证。这种特性使其...
  • 令牌登录方式流程(token)

    千次阅读 2020-08-10 16:31:40
    用户登录 ...6:将token跟用户对象使用json格式放回浏览器 浏览器: 接受到返回值,解析出token跟用户信息,并缓存 前端发起请求是,获取浏览器缓存的token值,通过请求头的方式携带到后端服务器 接口2:
  • #登录界面上HTML的信息,得到这个网站,有动态的token验证 X_Anti_Forge_Token = re.findall("X_Anti_Forge_Token = '(.*?)'",r1.text,re.S)[0] X_Anti_Forge_Code = re.findall("X_Anti_Forge_Code = '(.*?)'",r1....
  • 使用场景现在很多基于restful的api接口都有个登录的设计,也就是在发起正式的请求之前先通过一个登录的请求接口,申请一个叫做token的东西。申请成功后,后面其他的支付请求都要带上这个token,服务端通过这个token...
  • 无法获取token: 坚决方案:首先先检擦封装的登录接口和验证码接口是否有问题, 在main.js中引入 import ‘./http/request’ 在http文件夹中的request.js文件中创键一个axios实例 baseURL:env.prod.baseUrl+vipUrl ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 49,451
精华内容 19,780
关键字:

token格式登录