您的位置 首页 java

用户登录实现

用户抢单的时候,必须要先登录,我们先编写一个方法,用于实现用户登录,用户登录成功后,每次抢单的时候,还需要识别用户身份,我们这里采用JWT令牌保存用户身份信息,每次抢单识别JWT令牌即可。

2.1 Jwt令牌

JWT令牌这里我们将实现管理员令牌生成和普通用户令牌生成,管理员和普通用户他们生成了令牌的秘钥一定是不同的。

​ 在seckill-common工程中添加JWT令牌生成类com.seckill.util.JwtTokenUtil,在该类中实现令牌生成以及令牌解析,代码如下:

 public class JwtTokenUtil {

    //秘钥
    public  static  final String SECRETUSER="5pil6aOO5YaN576O5Lmf5q+U5LiN5LiK5bCP6ZuF55qE56yR";//用户
    public static final String SECRETADMIN="ADMIN5pil6aOO5YaN576O5Lmf5q+U5LiN5LiK5bCP6ZuF55qE56yR";//管理员

    /***
     * 生成令牌-管理员
     * @param uid:唯一标识符
     * @param ttlMillis:有效期
     * @return
     * @throws Exception
     */    public static String generateTokenAdmin(String uid, Map <String,Object> payload, long ttlMillis) throws  Exception  {
        return generateToken(uid,payload,ttlMillis,SECRETADMIN);
    }

    /***
     * 生成令牌-普通用户
     * @param uid:唯一标识符
     * @param ttlMillis:有效期
     * @return
     * @throws Exception
     */    public static String generateTokenUser(String uid,Map<String,Object> payload, long ttlMillis) throws Exception {
        return generateToken(uid,payload,ttlMillis,SECRETUSER);
    }

    /***
     * 生成令牌
     * @param uid:唯一标识符
     * @param ttlMillis:有效期
     * @return
     * @throws Exception
     */    public static String generateToken(String uid,Map<String,Object> payload, long ttlMillis,String secret) throws Exception {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        Key signingKey = new SecretKeySpec(secret.getBytes(), signatureAlgorithm.getJcaName());

        Map<String,Object>  header =new HashMap<String,Object>();
        header.put("typ","JWT");
        header.put("alg","HS256");
        JwtBuilder builder = Jwts.builder().setId(uid)
                .setIssuedAt(now)
                .setIssuer(uid)
                .setSubject(uid)
                .setHeader(header)
                .signWith(signatureAlgorithm, signingKey);

        //设置载体
        builder.addClaims(payload);

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

    /***
     * 解密JWT令牌
     */    public static Map<String, Object> parseToken(String token){
        //以Bearer开头处理
        if(token.startsWith("Bearer")){
            token=token.substring(6).trim();
        }

        //秘钥处理
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        Key signingKey = new SecretKeySpec(SECRETUSER.getBytes(), signatureAlgorithm.getJcaName());

        Claims claims = Jwts.parser()
                .setSigningKey(signingKey)
                .parseClaimsJws(token)
                .getBody();
        return claims;
    }
}
  

2.2 用户登录

​ 在seckill-user中实现用户登录,用户登录表机构如下:

 
CREATE TABLE `tb_user` (
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(100) NOT NULL COMMENT '密码,加密存储, MD5 加密',
  `phone` varchar(20) DEFAULT NULL COMMENT '注册手机号',
  `email` varchar(50) DEFAULT NULL COMMENT '注册邮箱',
  `created` datetime NOT NULL COMMENT '创建时间',
  `updated` datetime NOT NULL COMMENT '修改时间',
  `nick_name` varchar(50) DEFAULT NULL COMMENT '昵称',
  `name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
  PRIMARY KEY (`username`),
  UNIQUE KEY `username` (`username`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';
  

1)Service

在com.seckill.user.service.UserService中编写登录方法,代码如下:

 /**
 * 根据ID查询User
 * @param id
 * @return
 */ User findById(String id);
  

在com.seckill.user.service.impl.UserServiceImpl中编写登录方法实现,代码如下:

 
/**
 * 根据ID查询User
 * @param id
 * @return
 */@Override
public User findById(String id){
    return  userMapper.selectByPrimaryKey(id);
}
  

2)Controller

在com.seckill.user.controller.UserController中编写登录实现方法,代码如下:

 
/***
 * 根据ID查询User数据
 * @return
 */@GetMapping("/login")
public Result<User> findById(String username,String password) throws Exception {
    //调用UserService实现根据主键查询User
    User user = userService.findById(username);
    if(user==null){
        return new Result<User>(false,StatusCode.ERROR,"用户不存在");
    }

     if (!user.getPassword().equals(DigestUtils.md5DigestAsHex(password.getBytes()))){
        return new Result<User>(false,StatusCode.ERROR,"密码错误");
    }

    //登录成功,生成令牌
    Map<String,Object> payload = new HashMap<String,Object>();
    payload.put("username",user.getUsername());
    payload.put("name",user.getName());
    payload.put("phone",user.getPhone());

    //生成令牌
    String jwt =JwtTokenUtil.generateTokenUser(UUID. random UUID().toString(),payload, 900000L);
    return new Result<User>(true,StatusCode.OK,"登录成功",jwt);
}
  

我们可以生成一个令牌。

登录地址:

生成的令牌如下:

 {
    "flag": true,
    "code": 20000,
    "message": "登录成功",
    "data": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIyMmI5YzRjMy0yOTg5LTRkNTQtOGY3My0wYmI5MzAyMjMwYjQiLCJpYXQiOjE1ODczMjAxMDUsImlzcyI6IjIyYjljNGMzLTI5ODktNGQ1NC04ZjczLTBiYjkzMDIyMzBiNCIsInN1YiI6IjIyYjljNGMzLTI5ODktNGQ1NC04ZjczLTBiYjkzMDIyMzBiNCIsInBob25lIjoiMTM2NzAwODEzNzYiLCJuYW1lIjoi5rKI5Z2k5p6XIiwidXNlcm5hbWUiOiJpdGhlaW1hIiwiZXhwIjoxNTg4MjIwMTA1fQ.kzPYWLLnOtFBgedJZaiwzXnKFKHUnQXIqhWtCvl2zgk"
}
  

2.3 Jwt令牌识别

​ 识别Jwt令牌主要用于解析用户令牌,判断令牌是否真实有效。

2.3.1 流程分析

​ 前面我们编写的Java代码可以解析识别用户令牌,但我们现在要的流程如上图,用户请求 Nginx 执行抢单的时候,需要识别用户登录状态,如果已登录,则允许用户抢单,未登录是不允许用户抢单的,识别用户是否登录,我们这里采用 lua 脚本实现。

2.3.2 Lua识别Jwt令牌

​ 如果想使用Lua识别用户令牌,我们需要引入lua-resty-jwt模块,是用于 ngx_lua 和 LuaJIT 的 Lua 实现库,在该模块能实现Jwt令牌生成、Jwt令牌校验,依赖库的地址:

1)lua-resty-jwt安装

​ 在资料lua中已经下载好了该依赖库lua-resty-jwt-master.zip,我们将该库文件上传到服务器上,并解压,当然,我们也可以使用opm直接安装lua-resty-jwt,配置lua-resty-jwt之前,我们需要先安装resty和opm。

安装仓库管理工具包:

 yum install yum-utils
  

添加仓库地址:

 yum-config-manager --add-repo 
  

安装resty:

 yum install openresty-resty
  

安装opm:

 yum install openresty-opm
  

安装Jwt组件:

 opm get SkyLothar/lua-resty-jwt
  

此时lua-resty-jwt安装好了,可以直接使用了。

2)令牌识别

令牌识别有可能在很多操作都需要用到,所以我们可以创建一个独立的模块,用于识别令牌,文件名字叫token.lua

 
--依赖jwt库
local jwt = require("resty.jwt")
--秘钥
local secret="5pil6aOO5YaN576O5Lmf5q+U5LiN5LiK5bCP6ZuF55qE56yR"

-- 定义一个名为 jwttoken 的模块
jwttoken = {}

--令牌校验
function jwttoken.check(auth_header)
    --定义响应数据
    local response = {}

    --如果请求头中没有令牌,则直接返回401
    if auth_header == nil then
        response["code"]=401
        response["message"]="没有找到令牌数据"
        return response
    end

    --查找令牌中的Bearer前缀字符,并进行截取
    local _, _, token = string.find(auth_header, "Bearer%s+(.+)")

    --如果没有Bearer,则表示令牌无效
    if token == nil then
        response["code"]=401
        response["message"]="令牌格式不正确"
        return response
    end

    --校验令牌
    local jwt_obj = jwt:verify(secret, token)

    --如果校验结果中的verified==false,则表示令牌无效
    if jwt_obj.verified == false then
        response["code"]=401
        response["message"]="令牌无效"
        return response
    end

    --全部校验完成后,说明令牌有效,返回令牌数据
    response["code"]=200
    response["message"]="令牌校验通过"
    response["body"]=jwt_obj
    return response
end

return jwttoken
  

我们创建一个auth_verify.lua用于识别令牌,代码如下:

 
ngx.header.content_type="application/json;charset=utf8"

--引入json库
local cjson = require "cjson"

--引入jwt模块
local jwttoken = require "token"

--获取请求头中的令牌数据
local auth_header = ngx.var.http_Authorization

--调用令牌校验
local result = jwttoken.check(auth_header)

-- 输出结果
ngx.say(cjson.encode(result))
ngx.exit(result.code)
  

nginx.conf配置一个用于校验令牌的地址,代码如下:

 
#令牌校验
location /token {
    content_by_lua_file /usr/local/openresty/nginx/lua/auth_verify.lua;
}
  

3)令牌测试

我们用上面java生成的令牌进行测试,请求:测试令牌结果,如下图:

令牌错误输入,结果如下:

文章来源:智云一二三科技

文章标题:用户登录实现

文章地址:https://www.zhihuclub.com/183529.shtml

关于作者: 智云科技

热门文章

网站地图