一、执行注册流程
1. 目标:
如果针对注册操作所做的各项验证能够通过,则将Member信息存储到数据库中
2. 思路:
3. 代码
1) 设置数据表中loginacct 键 唯一
2) 编写mysql工程 的remote方法 【可以使用 postman 工具来测试】
/**
* 将用户信息添加到数据库表
* @param memberPO 用户信息
* @return
*/
@RequestMapping("/register/member/by/remote")
public ResultEntity<String> registerMember(@RequestBody MemberPO memberPO){
// DuplicateKeyException 账号被占用异常
try {
memberService.saveMember(memberPO);
}catch (Exception e){
if (e instanceof DuplicateKeyException){
return ResultEntity.failed(CrowdConstant.MESSAGE_LOGIN_ACCT_ALREADY_IN_USR);
}
return ResultEntity.failed(e.getMessage());
}
return ResultEntity.successWithoutData();
}
3) mysql工程的service方法
/**
* 因为类上面添加了 @Transactional(readOnly=true) 只读注解 所以这里需要重新改一下事务
* @param memberPO
* @return
*/
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
@Override
public Integer saveMember(MemberPO memberPO) {
int i = memberPOMapper.insertSelective(memberPO);
return i;
}
4) api工程的远程调用方法接口
/**
* 将用户信息添加到数据库表
* @param memberPO 用户信息
* @return
*/
@RequestMapping("/register/member/by/remote")
public ResultEntity<String> registerMember(@RequestBody MemberPO memberPO);
5) 创建MemberVo 类 【保存视图传过来的数据】
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MemberVO {
private String loginacct;
private String userpswd;
private String username;
private String email;
private String phoneNum;
private String code;
}
6) authentication-consumer的controller方法
这里需要使用重定向 ,如果就会出现二次提交表单的问题 远程调用的 mysql 和 redis
/**
* 用户注册
* @param memberVO 页面数据
* @param modelMap
* @return
*/
@RequestMapping("/auth/to/member/register")
public String toMemberRegister(MemberVO memberVO, ModelMap modelMap){
// 赋值属性 BeanUtils.copyProperties("源对象","目标对象");
// 1.获取用户的手机号
String phoneNum = memberVO.getPhoneNum();
// 2.拼接出redis 的key
String redisKey = CrowdConstant.REDIS_CODE_PREFIX + phoneNum;
// 3.在redis中查询获取key
ResultEntity<String> redisValueByKeyRemote = redisRemoteService.getRedisValueByKeyRemote(redisKey);
// 4.判断查询结果是否有效,如果无效带着message回到注册页面
if (ResultEntity.FAILED.equals(redisValueByKeyRemote.getResult())){
modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE,redisValueByKeyRemote.getMessage());
return "member-reg";
}
// 6.进行判断验证码
// 获取用户输入的验证码
String inputCode = memberVO.getCode();
// 获取系统的验证码
String systemCode = redisValueByKeyRemote.getData();
// 如果系统验证码为null,则验证码已过期【手机号错误】
if (systemCode == null){
modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE,CrowdConstant.MESSAGE_CODE_NOT_EXISTS);
return "member-reg";
}
// 如果验证码比对错误,则返回提示消息
if (!Objects.equals(systemCode,inputCode)){
modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE,CrowdConstant.MESSAGE_CODE_NOT_ERROR);
return "member-reg";
}
// 比对完成后,将redis中的 key删除,不然用户将再次注册 直接用这个验证码 就出现bug了
redisRemoteService.removeRedisValueByKeyRemote(redisKey);
// 7.如果一致,将保存数据库 并且先对密码进行加密
// 密码加密
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
// 获取密码
String inputPassword = memberVO.getUserpswd();
String encodePassword = bCryptPasswordEncoder.encode(inputPassword);
memberVO.setUserpswd(encodePassword);
// 将VO复制一份 PO
MemberPO memberPO = new MemberPO();
// 使用Spring提供的工具类
BeanUtils.copyProperties(memberVO, memberPO);
// 8.进行数据库保存操作
ResultEntity<String> stringResultEntity = mySqlRemoteService.registerMember(memberPO);
// 如果保存错误,返回注册页面并带回去信息
if (ResultEntity.FAILED.equals(stringResultEntity.getResult())){
modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE,stringResultEntity.getMessage());
return "member-reg";
}
System.out.println(stringResultEntity.getMessage());
// 使用重定向,将页面重定向到登录页面,避免F5刷新时在次提交表单
return "redirect:/auth/member/to/login/page";
}
7) 到登录页面的 view-controller
// 添加 登录的view-controller
registry.addViewController("/auth/member/to/login/page").setViewName("member-login");
8) 给consumer和zuul的配置文件中 修改ribbon超时时间,默认的时间太短
【关于第一次请求超时:由于在第一次请求中需要建立缓存,建立连接,操作较多,所以比较耗时,如果按照默认的ribbon超时时间来工作,第一次请求会操作或者时间导致超时报错,
为了解决这个问题,将ribbon的超时时间延长】
ribbon:
ReadTimeout: 10000
ConnectTimeout: 10000
二、登录
1. 目标:
用户登录成功后,将用户信息存储session 表示用户已经登录
2. 思路:
3. 代码
坑:这里比较密码使用BCryptPasswordEncoder对象的matches(form密码,数据库中密码) 方法
注意!:这里的form 不需要加密了,matches方法会自动加密
1) 修正一下 mysql-provider 的service方法 如果没查询到 直接返回null
@Override
public MemberPO getMemberByLoginAcct(String loginacct) {
// 创建Example
MemberPOExample memberPOExample = new MemberPOExample();
// 指定条件
MemberPOExample.Criteria criteria = memberPOExample.createCriteria();
criteria.andLoginacctEqualTo(loginacct);
List<MemberPO> memberPOList = memberPOMapper.selectByExample(memberPOExample);
if (memberPOList == null || memberPOList.size() == 0) {
return null;
}
return memberPOList.get(0);
}
2) MemberLoginVO 实体类 用来保存用户的登录信息
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MemberLoginVO {
private Integer Id;
private String loginacct;
private String username;
private String email;
}
3) authentication-consumer工程的controller方法 【登录路由】
/**
* 用户登录逻辑
* @param loginacct 登录账号
* @param userpswd 登录密码
* @param modelMap model map
* @param session session域
* @return
*/
@RequestMapping("/auth/member/do/login")
public String memberLogin(
@RequestParam("loginacct") String loginacct,
@RequestParam("userpswd") String userpswd,
ModelMap modelMap,
HttpSession session
){
// 1.根据账号去数据库查询
ResultEntity<MemberPO> memberByLoginAcctRemote = mySqlRemoteService.getMemberByLoginAcctRemote(loginacct);
// 判断
if (ResultEntity.FAILED.equals(memberByLoginAcctRemote.getResult())){
modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE,CrowdConstant.MESSAGE_LOGIN_FAILED);
return "member-login";
}
MemberPO memberPO = memberByLoginAcctRemote.getData();
if (memberPO == null){
modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE,CrowdConstant.MESSAGE_LOGIN_FAILED);
return "member-login";
}
// 对密码进行比对 , 如果不对返回到登录页面
String dataUserpswd = memberPO.getUserpswd();
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
boolean matches = bCryptPasswordEncoder.matches(userpswd, dataUserpswd);
if (!matches){
modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE,CrowdConstant.MESSAGE_LOGIN_FAILED);
return "member-login";
}
// 如果一致把对象转换成MemberLoginVO对象存到Session域中
MemberLoginVO memberLoginVO = new MemberLoginVO();
memberLoginVO.setId(memberPO.getId());
memberLoginVO.setLoginacct(memberPO.getLoginacct());
memberLoginVO.setUsername(memberPO.getUsername());
memberLoginVO.setEmail(memberPO.getEmail());
session.setAttribute(CrowdConstant.ATTR_NAME_LOGIN_MEMBER,memberLoginVO);
return "redirect:http://localhost/auth/member/to/center";
}
4) view-controller
// 添加 到用户主页的controller
registry.addViewController("/auth/member/to/center").setViewName("member-center");
5) 前端页面
适当位置使用[[${session.loginMember.username}]] 来获取存到session里面的用户信息
三、退出登录
1. 目标:
2. 思路:
调用 session对象的 invalidate()方法,清空session里面的值
3. 代码:
/**
* 用户退出逻辑
* @param session
* @return
*/
@RequestMapping("/auth/member/logout")
public String authMemberLogout(HttpSession session){
session.invalidate();
return "redirect:/auth/member/logout/to/portal";
}