SpringBoot学习项目-博客项目-part4

登陆

  1. 根据文档说明了登陆的时候的接口、请求方式以及请求的参数(String account、String password)

controller

  • 新创建一个LoginController,专门负责登陆功能
@RestController
@RequestMapping("login")
public class LoginController {

    @Autowired
    private LoginService loginService;

    @PostMapping
    public Result login(@RequestBody LoginParam loginParam){

        return loginService.login(loginParam);
    }
}
  • 同时封装一个LoginParam
@Data
public class LoginParam {
    private String account;
    private String password;
}
  1. 根据返回数据的形式,返回的是一个token,要想到JWT

JWT

  • 什么是JWT?
    1. JWT就理解为一个用户登陆令牌,在用户登陆的时候,可以生成一个加密的TOKEN,同时把这个TOKEN发给用户端。
    2. 请求需要登陆的资源或者接口的时候,要将TOKEN携带上,后端去验证token是否合法。
  • JWT的组成
    1. Header,{"type":"JWT","alg":"HS256"} 固定
    2. playload,存放信息,比如,用户id,过期时间等等,可以被解密,不能存放敏感信息
    3. 签名,Header和playload加上秘钥加密而成,只要秘钥不丢失,就可以认为是安全的。
      JWT验证主要就是验证签名部分是否合法

导入依赖包

  <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>

编写JWT的工具类

JWTUtils

  • 秘钥只能自己知道。
public class JWTUtils {

    private static final String jwtToken = "123456Mszlu!@#$$";

    public static String createToken(Long userId){
        Map<String,Object> claims = new HashMap<>();
        claims.put("userId",userId);
        JwtBuilder jwtBuilder = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, jwtToken) // 签发算法,秘钥为jwtToken
                .setClaims(claims) // body数据,要唯一,自行设置
                .setIssuedAt(new Date()) // 设置签发时间
                .setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000));// 一天的有效时间
        String token = jwtBuilder.compact();
        return token;
    }

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

    }

}

LoginService

  • 为什么不适用SysuserService?
    1. 因为每个表有每个表对应的一套流程,所以对于这个登录操作来说,应该新建立一个登陆的service专门来解决登陆的功能。
public interface LoginService {
    /**
     * 登录
     * @param loginParam
     * @return
     */
    Result login(LoginParam loginParam);
}

LoginServiceImpl

  • 分析在实现类的具体动作
    1. 根据传入的登陆参数,可以获取到用户账户、密码。
    2. 密码不能以明文的形式报文在数据库中,要使用算法进行加密,加密算法为MD5加盐。
    3. 加密完成之后,要去用户表中找,根据账户和密码
    4. 如果返回为空,那么就返回账户名密码不存在的信息。
    5. 返回不为空,那么说明有该账户,使用JWT去生成一个token,同时将token保存到redis中,根据返回数据看,最后返回的也是token。
    6. 这里因为是登陆设计到了用户表,肯定需要sysUserService
@Service
public class LoginServiceImpl implements LoginService {

    private static final String slat = "mszlu!@#";
    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public Result login(LoginParam loginParam) {
        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 pwd = DigestUtils.md5Hex(password + slat);
        SysUser sysUser = sysUserService.findUser(account,pwd);
        if (sysUser == null){
            return Result.fail(ErrorCode.ACCOUNT_PWD_NOT_EXIST.getCode(),ErrorCode.ACCOUNT_PWD_NOT_EXIST.getMsg());
        }
        //登录成功,使用JWT生成token,返回token和redis中
        String token = JWTUtils.createToken(sysUser.getId());
        redisTemplate.opsForValue().set("TOKEN_"+token, JSON.toJSONString(sysUser),1, TimeUnit.DAYS);
        return Result.success(token);
    }

    public static void main(String[] args) {
        System.out.println(DigestUtils.md5Hex("admin"+slat));
    }
}

SysUserService

//根据用户名和密码查找用户
SysUser findUser(String account, String password);

SysUserServiceImpl

@Override
    public SysUser findUser(String account, String password) {
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysUser::getAccount,account);
        wrapper.eq(SysUser::getPassword, password);
        wrapper.select(SysUser::getAccount, SysUser::getId,SysUser::getAvatar,SysUser::getNickname);
//        加快查询策略
        wrapper.last("limit 1");
        return sysUserMapper.selectOne(wrapper);
    }

redis

  1. 为什么要将token保存到redis?
    (1)token具有时效性,用redis处理有优势
    (2)登陆信息一般不需要长效存储
    (3)如果出现跨设备了,设备A登陆收到一个TOken,设备B登陆收到一个Token,这样两个设备可以同时在同一个应用上进行登陆,不合理。使用redis限制。即:单点登陆
  2. redis的一些配置

application.properties

spring.redis.host=localhost
spring.redis.port=6379
  1. 同时要下载安装redis
    redis

统一异常错误码

  • 这是干什么用的?前面不是有定义统一返回结果了嘛?
  • 前面的统一返回结果状态码是用来前后端的。这里的异常码是用来处理登陆的时候的异常,比如可能出现的,传入的参数有问题、token过期、用户名或者密码不存在等问题
public enum  ErrorCode {

    PARAMS_ERROR(10001,"参数有误"),
    ACCOUNT_PWD_NOT_EXIST(10002,"用户名或密码不存在"),
    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;
    }
}
  • 前端获取到了token之后,会存储在storage中H5(本地存储)
    SpringBoot学习项目-博客项目-part4
上一篇:万字详细解释cookie,session,JWT(token)


下一篇:JWT 认证