尚筹网2-3.基于令牌的登陆功能

1.基于令牌的登录机制

①好处

  • 隐藏用户的私密、关键信息
  • 让浏览器端数据存储可以不依赖于Cookie,服务器端可以不依赖于Session

②token存储的两种方式

[1]使用Cookie、Session机制存储
[2]使用基于HTML5的存储对象存储

W3CSchool相关参考文档

对象名 作用
localStorage 没有时间限制的数据存储,对应整个Web应用,相当于服务端域对象中的application
sessionStorage 针对一个 session 的数据存储,当用户关闭浏览器窗口后,数据会被删除。相当于服务端域对象中的session

2.具体实现

①执行流程

  • 接收表单数据
    • 登录账号
    • 密码
  • 根据登录账号查询MemberPO对象
    • 没有查询到:登录失败
    • 查询到对象:继续执行
  • 比较密码
    • 不一致:登录失败
    • 一致:继续执行
  • 生成token值
  • 将token拼接固定前缀作为KEY,将MemberId作为VALUE存入Redis
  • 封装MemberSignSuccessVO对象
    • 用户昵称:username
    • 邮箱地址:email
    • 令牌:token
  • 返回MemberSignSuccessVO对象

②根据登录账号查询MemberPO对象

[1]接口方法

所在工程:distribution-crowd-1-common

所在接口:com.rgh.crowd.api.DataBaseOperationRemoteService

@RequestMapping("/retrieve/member/by/login/acct")
ResultEntity retrieveMemberByLoginAcct(@RequestParam("loginAcct") String loginAcct);

[2]与接口对应的Controller方法

所在工程:distribution-crowd-3-database-provider

所在类:com.rgh.crowd.controller.MemberController

@RequestMapping("/retrieve/member/by/login/acct")
public ResultEntity retrieveMemberByLoginAcct(@RequestParam("loginAcct") String loginAcct) {
    MemberPO memberPO = null;
    try {
        memberPO = memberService.getMemberByLoginAcct(loginAcct);
    } catch (Exception e) {
        e.printStackTrace();
        return ResultEntity.failed(e.getMessage());
    }
    return ResultEntity.successWithData(memberPO);
}

Service方法

@Override
public MemberPO getMemberByLoginAcct(String loginAcct) {
    // 1.创建MemberPOExample对象
    MemberPOExample example = new MemberPOExample();
    // 2.封装查询条件
    example.createCriteria().andLoginacctEqualTo(loginAcct);
    // 3.执行QBC查询
    List memberList = memberPOMapper.selectByExample(example);
    // 4.判断查询结果集合的有效性
    if(CrowdUtils.collectionEffectiveCheck(memberList)) {
        // 5.如果查询结果集合有效,则返回第一个元素
        return memberList.get(0);
    }
    // 6.如果集合无效则返回null
    return null;
}

③生成token的工具方法

所在工程:distribution-crowd-1-common

所在类:com.rgh.crowd.util.CrowdConstant

public static final String REDIS_MEMBER_SING_TOKEN_PREFIX = "SIGNED_MEMBER_";

所在类:com.rgh.crowd.util.CrowdUtils

/**
 * 生成用户登录成功后使用的token
 * @return
 * @author 封捷
 */
public static String generateToken() {
    return CrowdConstant.REDIS_MEMBER_SING_TOKEN_PREFIX + UUID.randomUUID().toString().replaceAll("-", "");
}

⑤封装MemberSignSuccessVO类

所在工程:distribution-crowd-1-common

所在包:com.rgh.crowd.entity.vo

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MemberSignSuccessVO {
    
    private String username;
    private String email;
    private String token;

}
  • 在entity后新建vo和po两个包,把entity对应的实体类移入到对应的包中
  • 将逆向工程中实体类生成的包改为entity.po

⑥具体执行登录的方法

所在工程:distribution-crowd-5-member-manager

所在类:com.rgh.crowd.controller.MemberController

@RequestMapping("/member/manager/login")
public ResultEntity login(
    @RequestParam("loginAcct") String loginAcct, 
    @RequestParam("userPswd") String userPswd) {
    // 1.根据登录账号查询数据库获取MemberPO对象
    ResultEntity resultEntity = dataBaseOperationRemoteService.retrieveMemberByLoginAcct(loginAcct);
    if(ResultEntity.FAILED.equals(resultEntity.getResult())) {
        return ResultEntity.failed(resultEntity.getMessage());
    }
    // 2.获取MemberPO对象
    MemberPO memberPO = resultEntity.getData();
    // 3.检查MemberPO对象是否为空
    if(memberPO == null) {
        return ResultEntity.failed(CrowdConstant.MESSAGE_LOGIN_FAILED);
    }
    // 4.获取从数据库查询得到的密码
    String userpswdDatabase = memberPO.getUserpswd();
    // 5.比较密码
    boolean matcheResult = passwordEncoder.matches(userPswd, userpswdDatabase);
    if(!matcheResult) {
        return ResultEntity.failed(CrowdConstant.MESSAGE_LOGIN_FAILED);
    }
    // 6.生成token
    String token = CrowdUtils.generateToken();
    // 7.从MemberPO对象获取memberId
    String memberId = memberPO.getId() + "";
    // 8.将token和memberId存入Redis
    ResultEntity resultEntitySaveToken = redisRemoteService.saveNormalStringKeyValue(token, memberId, 30);
    if(ResultEntity.FAILED.equals(resultEntitySaveToken.getResult())) {
        return ResultEntity.failed(resultEntitySaveToken.getMessage());
    }
    // 9.封装MemberSignSuccessVO对象
    MemberSignSuccessVO memberSignSuccessVO = new MemberSignSuccessVO();
    BeanUtils.copyProperties(memberPO, memberSignSuccessVO);
    memberSignSuccessVO.setToken(token);
    // 10.返回结果
    return ResultEntity.successWithData(memberSignSuccessVO);
}

注意:修改database-provider中MemberPOMapper.xml的namespace路径和paramType的类型,IDEA会自动加上

测试:数据库中给marry加上用户名和昵称

3.退出登录

所在工程:distribution-crowd-5-member-manager

所在类:com.rgh.crowd.controller.MemberController

@RequestMapping("/member/manager/logout")
public ResultEntity logout(@RequestParam("token") String token) {
    return redisRemoteService.removeByKey(token);
}

测试功能:

  1. 发送验证码

在Redis中确认验证码已经保存

  1. 测试注册功能

看数据库是否已经存入

  1. 测试登陆功能
  1. 根据token去Redis中查对应的值
  1. 测试退出功能

看Redis中的token令牌是否删除

你可能感兴趣的:(尚筹网2-3.基于令牌的登陆功能)