在线博客系统——登录

目录

接口说明

编码实现

JWT依赖包

JWTUtils工具类

Controller控制层

Service业务逻辑层

Vo对象

前端测试

JWT技术

Header

Payload

Signature


接口说明

接口url:/login

请求方式:POST

请求参数:

参数名称 参数类型 说明
account string 账号
password string 密码

返回数据:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": "token"
}

编码实现

本项目使用JWT生成token,并将token作为key在redis中保存用户信息,目的是:

有可能在用户退出登录前token被盗取,加入redis中再次确认

加入redis你退出登录的时候也清除redis

JWT依赖包

  
        io.jsonwebtoken
        jjwt
        0.9.1
    

JWTUtils工具类

package com.huing.blog.utils;

import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * jwt 可以生成 一个加密的token,做为用户登录的令牌,当用户登录成功之后,发放给客户端。
 *
 *  请求需要登录的资源或者接口的时候,将token携带,后端验证token是否合法。
 *
 *  jwt 有三部分组成:A.B.C
 *
 *  A:Header,{“type”:“JWT”,“alg”:“HS256”} 固定
 *
 *  B:playload,存放信息,比如,用户id,过期时间等等,可以被解密,不能存放敏感信息
 *
 *  C: 签证,A和B加上秘钥 加密而成,只要秘钥不丢失,可以认为是安全的。
 *
 *  jwt 验证,主要就是验证C部分 是否合法。
 *
 * @author huing
 * @create 2022-07-04 15:53
 */

public class JWTUtils {

    /**
     * 密钥
     */
    private static final String jwtToken = "123456Huing!@#$$";

    public static String createToken(Long userId) {
        /**
         * B部分
         */
        Map claims = new HashMap<>();
        claims.put("userId", userId);


        JwtBuilder jwtBuilder = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, jwtToken) // 签发算法,秘钥为jwtToken     A部分
                .setClaims(claims) // body数据,要唯一,自行设置                               B部分
                .setIssuedAt(new Date()) // 设置签发时间
                .setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000));// 一天的有效时间
        String token = jwtBuilder.compact();
        return token;
    }

    public static Map checkToken(String token) {
        try {
            Jwt parse = Jwts.parser().setSigningKey(jwtToken).parse(token);
            return (Map) parse.getBody();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

Controller控制层

package com.huing.blog.controller;

import com.huing.blog.service.LoginService;
import com.huing.blog.vo.Result;
import com.huing.blog.vo.params.LoginParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author huing
 * @create 2022-07-04 15:27
 */
@RestController
@RequestMapping("login")
public class LoginController {

    @Autowired
    private LoginService loginService;

    /**
     * JWT登录
     *
     * @param loginParam
     * @return
     */
    @PostMapping
    public Result login(@RequestBody LoginParam loginParam){
        return loginService.login(loginParam);
    }
}

Service业务逻辑层

LoginService接口:

package com.huing.blog.service;

import com.huing.blog.vo.Result;
import com.huing.blog.vo.params.LoginParam;

/**
 * @author huing
 * @create 2022-07-04 15:29
 */
public interface LoginService {
    /**
     * JWT登录
     *
     * @param loginParam
     */
    Result login(LoginParam loginParam);
}

LoginServiceImpl实现类:

package com.huing.blog.service.impl;

import com.alibaba.fastjson.JSON;
import com.huing.blog.dao.mapper.SysUserMapper;
import com.huing.blog.dao.pojo.SysUser;
import com.huing.blog.service.LoginService;
import com.huing.blog.service.SysUserService;
import com.huing.blog.utils.JWTUtils;
import com.huing.blog.vo.ErrorCode;
import com.huing.blog.vo.Result;
import com.huing.blog.vo.params.LoginParam;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * @author huing
 * @create 2022-07-04 15:29
 */
@Service
public class LoginServiceImpl implements LoginService {

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${user.pwd.salt}")
    private String salt;

    @Override
    public Result login(LoginParam loginParam) {
        /**
         * 1.检查参数是否合法
         * 2.根据用户名和密码去user表查询 是否存在
         * 3.不存在,登陆失败
         * 4.存在,使用JWT   生成token     返回给前端
         * 5.token放入redis中,redis    token: user信息   设置过期时间(登录认证的时候,先认证token字符串是否合法,去redis认证是否合法)
         */
        String account = loginParam.getAccount();
        String password = loginParam.getPassword();
        if (StringUtils.isBlank(account) || StringUtils.isBlank(password)) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), ErrorCode.PARAMS_ERROR.getMsg());
        }
        String newPwd = DigestUtils.md5Hex(password + salt);
        SysUser user = sysUserService.findUser(account, newPwd);
        if (user == null) {
            return Result.fail(ErrorCode.ACCOUNT_PWD_NOT_EXIST.getCode(), ErrorCode.ACCOUNT_PWD_NOT_EXIST.getMsg());
        }
        // 使用JWT   生成token
        String token = JWTUtils.createToken(user.getId());

        //有可能在你退出登录前token被盗取,加入redis你退出登录的时候也清除redis
        //token放入redis中,过期时间是一天
        redisTemplate.opsForValue().set("TOKEN_" + token, JSON.toJSONString(user),1, TimeUnit.DAYS);

        return Result.success(token);
    }
}

sysUserService.findUser(account, newPwd)接口:

    /**
     * 根据用户名密码查询用户数据
     * @param account
     * @param newPwd
     * @return
     */
    SysUser findUser(String account, String newPwd);

sysUserService.findUser(account, newPwd)实现类:

    @Override
    public SysUser findUser(String account, String newPwd) {
        LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUser::getAccount,account);
        queryWrapper.eq(SysUser::getPassword,newPwd);
        queryWrapper.select(SysUser::getAccount,SysUser::getId,SysUser::getAvatar,SysUser::getNickname);
        queryWrapper.last("limit 1");
        SysUser sysUser = sysUserMapper.selectOne(queryWrapper);
        return sysUser;
    }

application.properties

#   spring读取配置文件的数据:@Value("${tag.hot.limit}")
#设置tag最热标签个数
tag.hot.limit=6
#设置Article最热文章个数
article.hotAndNew.limit=6
#设置pwd密码加密盐
user.pwd.salt=huing!@#



# redis配置
spring.redis.host=huing100
spring.redis.port=6379

Vo对象

LoginParam:

package com.huing.blog.vo.params;

import lombok.Data;

/**
 * @author huing
 * @create 2022-07-04 15:33
 */
@Data
public class LoginParam {

    private String account;

    private String password;

    private String nickname;
}

ErrorCode:

package com.huing.blog.vo;

/**
 * @author huing
 * @create 2022-07-04 15:40
 */
public enum ErrorCode {
    PARAMS_ERROR(10001,"参数有误"),
    ACCOUNT_PWD_NOT_EXIST(10002,"用户名或密码不存在"),
    TOKEN_ERROR(10003,"token不合法"),
    ACCOUNT_EXIST(10004,"账号已存在"),
    NO_PERMISSION(70001,"无访问权限"),
    SESSION_TIME_OUT(90001,"会话超时"),
    NO_LOGIN(90002,"未登录");

    private int code;
    private String msg;

    ErrorCode(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

前端测试

使用postman工具:

在线博客系统——登录_第1张图片

JWT技术

JWT的本质就是一个字符串,它是将用户信息保存到一个Json字符串中,然后进行编码后得到一个Jwt token,并且这个Jwt token带有签名信息,接收后可以校验是否被篡改,所以可以用于在各方之间安全地将信息作为Json对象传输。

JWT由3部分组成:标头(Header)、有效载荷(Payload)和签名(Signature)。在传输的时候,会将JWT的3部分分别进行Base64编码后用.进行连接形成最终传输的字符串

{“type”:“JWT”,“alg”:“HS256”} 固定,JWT头是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型。

Payload

有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择:

  • iss:发行人
  • exp:到期时间
  • sub:主题
  • aud:用户
  • nbf:在此之前不可用
  • iat:发布时间
  • jti:JWT ID用于标识该JWT
     

存放信息,比如,用户id,过期时间等等,可以被解密,不能存放敏感信息

Signature

签名哈希部分是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。首先,需要指定一个密钥(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。

只要秘钥不丢失,可以认为是安全的。

jwt 验证,主要就是验证C部分 是否合法。

jwt 生成 一个加密的token,做为用户登录的令牌,当用户登录成功之后,发放给客户端。

请求需要登录的资源或者接口的时候,将token携带,后端验证token是否合法。

你可能感兴趣的:(java,spring,boot,mybatis,spring)