0
点赞
收藏
分享

微信扫一扫

个人博客项目——登录和注册

鱼满舱 2022-03-30 阅读 59

五、登录和注册

1、登录

1.1接口说明

接口url:/login

请求方式:POST

请求参数:

参数名称参数类型说明
accountstring账号
passwordstring密码

返回数据:

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

1.2 JWT

登录使用JWT技术。

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

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

jwt 有三部分组成:A.B.C

A:Header,{“type”:“JWT”,“alg”:“HS256”} 固定

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

C: 签证,A和B加上秘钥 加密而成,只要秘钥不丢失,可以认为是安全的。

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

依赖包:

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

工具类:

package com.mszlu.blog.utils;

import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

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

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

    }

}

1.3 controller

package com.ling.blog.controller;

import com.ling.blog.service.LoginService;
import com.ling.blog.vo.Result;
import com.ling.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;

@RestController
@RequestMapping("/login")
public class LoginController {
    @Autowired
    private LoginService loginService;
    @PostMapping
    public Result login(@RequestBody LoginParam loginParam){
        return loginService.login(loginParam);
    }
}

1.4 service

package com.ling.blog.service;

import com.ling.blog.dao.pojo.SysUser;
import com.ling.blog.vo.Result;
import com.ling.blog.vo.params.LoginParam;

public interface LoginService {

    /**
     * 登录功能
     * @param loginParam
     * @return
     */
    Result login(LoginParam loginParam);
package com.ling.blog.service.impl;

import com.alibaba.fastjson.JSON;
import com.ling.blog.dao.pojo.SysUser;
import com.ling.blog.service.LoginService;
import com.ling.blog.service.SysUserService;
import com.ling.blog.utils.JWTUtils;
import com.ling.blog.vo.ErrorCode;
import com.ling.blog.vo.Result;
import com.ling.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.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@Transactional
public class LoginServiceImpl implements LoginService {
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    private static final String slat = "mszlu!@#";
    @Override
    public Result login(LoginParam loginParam) {
        //1、检测参数是否合法
        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());
        }
        //密码经过处理,使用加密盐
        password = DigestUtils.md5Hex(password + slat);
        //2、根据用户名和密码去user表中查询是否存在
        SysUser sysUser = sysUserService.findUser(account,password);
        //3、如果不存在,登录失败
        if(sysUser == null){
            return Result.fail(ErrorCode.ACCOUNT_PWD_NOT_EXIST.getCode(),ErrorCode.ACCOUNT_PWD_NOT_EXIST.getMsg());
        }
        //4、存在,使用jwt生成token,返回给前端
        String token = JWTUtils.createToken(sysUser.getId());
        //5、token放入redis中,redis存储token:user信息 设置过期时间
        redisTemplate.opsForValue().set("TOKEN_"+token, JSON.toJSONString(sysUser),1, TimeUnit.DAYS);
        // (登陆认证时,先认证token字符串是否合法,去redis认证是否存在)

        return Result.success(token);
    }
}

md5加密的依赖包:

  <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
    </dependency>

SysUserServiceImpl:

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

1.5 vo

package com.ling.blog.vo.params;

import lombok.Data;

@Data
public class LoginParam {
    private String account;
    private String password;
}

配置redis

spring.redis.host=localhost
spring.redis.port=6379
package com.mszlu.blog.vo;

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

2、获取登录用户数据

2.1 接口说明

接口url:/users/currentUser

请求方式:GET

请求参数:

参数名称参数类型说明
Authorizationstring头部信息(TOKEN)

返回数据:

{
    "success": true,
    "code": 200,
    "msg": "success",
    "data": {
        "id":1,
        "account":"1",
        "nickaname":"1",
        "avatar":"ss"
    }
}

2.2 controller

package com.ling.blog.controller;

import com.ling.blog.service.SysUserService;
import com.ling.blog.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private SysUserService sysUserService;

    @GetMapping("/currentUser")
    public Result currentUser(@RequestHeader("Authorization") String token){
        return sysUserService.findUserByToken(token);
    }
}

2.3 service

@Override
public Result findUserByToken(String token) {
    /**
     * 1、token合法性校验
     * 是否为空,解析是否成功 redis是否存在
     * 2、如果校验失败,返回错误
     * 3、如果成功,返回对应结果 LoginUserVo
     */
    SysUser sysUser = loginService.checkToken(token);
    if(sysUser == null){
        return Result.fail(ErrorCode.TOKEN_ERROR.getCode(), ErrorCode.TOKEN_ERROR.getMsg());
    }
    LoginUserVo loginUserVo = new LoginUserVo();
    loginUserVo.setId(sysUser.getId());
    loginUserVo.setNickname(sysUser.getNickname());
    loginUserVo.setAccount(sysUser.getAccount());
    loginUserVo.setAvatar(sysUser.getAvatar());
    return Result.success(loginUserVo);
}

2.4 vo

package com.ling.blog.vo;

import lombok.Data;

@Data
public class LoginUserVo {
    private Long id;

    private String account;

    private String nickname;

    private String avatar;
}

3 退出登录

3.1 接口说明

接口url:/logout

请求方式:GET

请求参数:

参数名称参数类型说明
Authorizationstring头部信息(TOKEN)

返回数据:

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

3.2 controller

package com.ling.blog.controller;

import com.ling.blog.service.LoginService;
import com.ling.blog.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/logout")
public class LogoutController {
    @Autowired
    private LoginService loginService;

    @GetMapping
    public Result logout(@RequestHeader("Authorization") String token){
        return loginService.logout(token);
    }
}

3.3 service

/**
 * 退出登录
 * @param token
 * @return
 */
@Override
public Result logout(String token) {
    redisTemplate.delete("TOKEN_"+token);
    return Result.success(null);
}

4、注册

4.1 接口说明

接口url:/register

请求方式:POST

请求参数:

参数名称参数类型说明
accountstring账号
passwordstring密码
nicknamestring昵称

返回数据:

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

4.2 controller

package com.ling.blog.controller;

import com.ling.blog.service.LoginService;
import com.ling.blog.vo.Result;
import com.ling.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;

@RestController
@RequestMapping("/register")
public class RegisterController {
    @Autowired
    private LoginService loginService;
    @PostMapping
    public Result register(@RequestBody LoginParam loginParam){
        //sso 单点登录,后期如果把登录注册功能提出去(单独的服务,可以提供独立接口的服务)
        return loginService.register(loginParam);
    }
}

参数LoginParam中 添加新的参数nickname。

4.3 service

/**
 * 注册功能
 * @param loginParam
 * @return
 */
@Override
public Result register(LoginParam loginParam) {
    /**
     * 1、判断参数是否合法
     * 2、判断账户是否存在 存在,返回账户已经被注册
     * 3、不存在,注册用户
     * 4、生成token
     * 5、存入redis并返回
     * 6、注意,加上事务,一旦中间的任何过程出现问题,需要回滚
     */
    String account = loginParam.getAccount();
    String password = loginParam.getPassword();
    String nickname = loginParam.getNickname();
    if(StringUtils.isBlank(account) || StringUtils.isBlank(password) || StringUtils.isBlank(nickname)){
        return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), ErrorCode.PARAMS_ERROR.getMsg());
    }
    SysUser sysUser = sysUserService.findUserByAccount(account);
    if(sysUser != null){
        return Result.fail(ErrorCode.ACCOUNT_EXIST.getCode(), ErrorCode.ACCOUNT_EXIST.getMsg());
    }
    sysUser = new SysUser();
    sysUser.setNickname(nickname);
    sysUser.setAccount(account);
    sysUser.setPassword(DigestUtils.md5Hex(password+slat));
    sysUser.setCreateDate(System.currentTimeMillis());
    sysUser.setLastLogin(System.currentTimeMillis());
    sysUser.setAvatar("/static/img/logo.b3a48c0.png");
    sysUser.setAdmin(1); //1 为true
    sysUser.setDeleted(0); // 0 为false
    sysUser.setId(null);
    sysUser.setMobilePhoneNumber("111");
    sysUser.setSalt("111");
    sysUser.setStatus("111");
    sysUser.setEmail("111");
    this.sysUserService.save(sysUser);

    String token = JWTUtils.createToken(sysUser.getId());
    //注册好像不可以设置过期时间
    redisTemplate.opsForValue().set("TOKEN_"+token, JSON.toJSONString(sysUser));

    return Result.success(token);
}

ErrorCode中添加:

ACCOUNT_EXIST(10004,"账号已存在"),

SysUserServiceImpl:

/**
 * 根据账户查询用户
 * @param account
 * @return
 */
@Override
public SysUser findUserByAccount(String account) {
    /*LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(SysUser::getAccount,account);
    queryWrapper.last("limit 1");
    return sysUserMapper.selectOne(queryWrapper);*/
    return sysUserMapper.findUserByAccount(account);
}

@Override
public void save(SysUser sysUser) {
    //保存用户,id会自动生成
    //默认生成的id是分布式id,采用雪花算法
    //this.sysUserMapper.insert(sysUser);
   // String account = sysUser.getAccount();
    sysUserMapper.addUser(sysUser);
}

4.4 dao

/**
 * 根据账户名查询用户
 * @param account
 * @return
 */
SysUser findUserByAccount(@Param("account")String account);

/**
 * 添加用户,用于注册用户的保存
 * @param sysUser
 */
void addUser(@Param("sysUser") SysUser sysUser);
<!--SysUser findUserByAccount(@Param("account")String account);-->
<select id="findUserByAccount" parameterType="string" resultType="com.ling.blog.dao.pojo.SysUser">
    select *from ms_sys_user where account = #{account}
</select>

<!--void addUser(SysUser sysUser);-->
<insert id="addUser">
    insert into ms_sys_user
        values (null,#{sysUser.account},#{sysUser.admin},#{sysUser.avatar},#{sysUser.createDate},
                #{sysUser.deleted},#{sysUser.email},#{sysUser.lastLogin},#{sysUser.mobilePhoneNumber},
                #{sysUser.nickname},#{sysUser.password},#{sysUser.salt},#{sysUser.status})
</insert>

4.5加事务

@Service
@Transactional
public class LoginServiceImpl implements LoginService {}
举报

相关推荐

0 条评论