Day 15 - Spring Boot 注册与登入

前面几篇已经完成了资料库的基本操作跟使用Thymeleaf 呈现页面,接下来才真正要踏入Spring Boot 的世界,我们会围绕在注册、登入与会员中心三个基本功能,从最简单的资料库操作,逐步加上资料验证、例外处理、登入日志等功能。
记得相关的注释在Day 09 - Spring Boot 常用注释(下)都有提到,由於篇幅的关系,这边就不阐述开发时的测试程序,主要讲Controller 和Service 这两层的内容,对测试有兴趣的话可以去看Github。

控制器层

首先,要在控制器层的类别上新增注释@Controller,宣告这是SpringMVC 的Controller 物件,才继续往下设定请求的路径,其实在上一篇Day 14 - Spring Boot & Thymeleaf已经建立了显示注册与登入页面的Controller,就在里面新增两个POST 的方法,用来接收注册与登入时的送出的表单。

@Controller
public class MemberAccountController {

	@Autowired
	private MemberAccountService memberAccountService;
	
	@RequestMapping(value = {"/", "/login"}, method = RequestMethod.GET)
	public String login(
			@ModelAttribute MemberAccount memberAccount,
			@ModelAttribute(value = "MESSAGE") String message) {
		
		return "login";
	}
	
	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public String doLogin(
			@ModelAttribute MemberAccount memberAccount,
			HttpSession session, 
			RedirectAttributes redirectAttributes) {
		
		MemberAccountVO memberAccountVO = memberAccountService.login(memberAccount);
		if(memberAccountVO == null) {
			String message = memberAccountVO == null ? "帐号或密码错误" : "";	
			redirectAttributes.addFlashAttribute("MESSAGE", message);
			return "redirect:login";
		}
		session.setAttribute("member", memberAccountVO);	
		return "redirect:information";
	}
	
	@RequestMapping(value = "/register", method = RequestMethod.GET)
	public String register(@ModelAttribute MemberAccountVO memberAccountVO) {
		
		return "register";
	}
	
	@RequestMapping(value = "/register", method = RequestMethod.POST)
	public String doRegister(
			@ModelAttribute MemberAccountVO memberAccountVO,
			RedirectAttributes redirectAttributes) {

		Optional<String> optional = memberAccountService.register(memberAccountVO);
		String message = optional.orElse("注册成功");
		redirectAttributes.addFlashAttribute("MESSAGE", message);
		return "redirect:login";
	}
	
}

业务逻辑层

业务逻辑层的地方跟持久层都一样,先定义好介面再撰写实作该介面的具体类别,或许有些人会觉得这样很麻烦,不能直接写个类别吗,这个原因其实并不复杂,推荐可以看一下Java 为什麽在Service层要使用Interface 这篇文章,作者有详细解释及实作讲解为什麽要使用介面。

业务逻辑层介面

Day 06 - MVC 与三层架构已经提过的,三层架构的业务逻辑层与持久层都是对应MVC 架构中的M的部分,在业务逻辑层的介面这边,可以用注解分类方法的用途

public interface MemberAccountService {

	// 业务逻辑
	public MemberAccountVO login(MemberAccount memberAccount);
	public Optional<String> register(MemberAccountVO memberAccountVO);
	
	// 资料库操作
	public MemberAccount findMemberAccountByUsername(String username);	

}

业务逻辑层实作

在业务逻辑层的实作类别上,记得先新增注释@Service,宣告这是业务处理类别,再依照逻辑需求使用@Autowired 注入所需要的物件,而实际上这边应该要在每个方法上面都写上很多注解标明这个方法的用途、引数以及回传值,但这边为了不让文章太长就不写了,就先记得在撰写方法的逻辑前要列出该方法的逻辑顺序

撰写业务逻辑前

@Override
public MemberAccountVO login(MemberAccount memberAccount) {
	// TODO Auto-generated method stub
	// 检查帐号是否存在

	// 使用资料库盐值对输入密码进行加密

	// 比对密码是否相等

	// 取得对应Member 资料

	// 组合资料为MemberAccountVO

	return null;
}

完成

@Service
public class MemberAccountServiceImpl implements MemberAccountService {

	@Autowired
	private MemberAccountDao memberAccountDao;

	@Autowired
	private MemberService memberService;
	
	private String getMd5Password(String password, String salt) {
		// 对password + salt 进行三次加密
		String str = password + salt;
		for (int i = 0; i < 3; i++) {
			str = DigestUtils.md5DigestAsHex(str.getBytes()).toUpperCase();
		}
		return str;
	}
	
	@Override
	public MemberAccountVO login(MemberAccount memberAccount) {
		// TODO Auto-generated method stub
		// 检查帐号是否存在
		MemberAccount data = memberAccountDao.findMemberAccountByUsername(memberAccount.getUsername());
		if(data == null) return null;

		// 使用资料库盐值对输入密码进行加密
		String md5Password = getMd5Password(memberAccount.getPassword(), data.getSalt());

		// 比对密码是否相等
		if(!md5Password.equals(data.getPassword())) return null;
		
		// 取得对应Member 资料
		Member member = memberService.findMemberByMa_id(data.getId());
		if(member == null) return null;
		
		// 组合资料为MemberAccountVO
		MemberAccountVO memberAccountVO = new MemberAccountVO();
		memberAccountVO.setUsername(memberAccount.getUsername());
		memberAccountVO.setName(member.getName());
		return memberAccountVO;
	}

	@Override
	public Optional<String> register(MemberAccountVO memberAccountVO) {
		// TODO Auto-generated method stub
		// 验证栏位是否填写及格式
		if(!ValidFormat.isEmail(memberAccountVO.getUsername())) return Optional.of("帐号必须是Email 格式");
		if(!ValidFormat.isPassword(memberAccountVO.getPassword())) return Optional.of("密码必须为长度6~16位码大小写英文加数字");
		if(!memberAccountVO.getPassword().equals(memberAccountVO.getCheckPassword())) return Optional.of("两次输入密码不相符");
		
		// 检查帐号是否重复注册
		MemberAccount data = memberAccountDao.findMemberAccountByUsername(memberAccountVO.getUsername());
		if(data != null) return Optional.of("该帐号已被使用");
		
		// 产生盐值
		String salt = UUID.randomUUID().toString().toUpperCase().replaceAll("-", "");
		
		// 密码加密
		String md5Password = getMd5Password(memberAccountVO.getPassword(), salt);

		// 新增MemberAccount 资料
		MemberAccount memberAccount = new MemberAccount();
		memberAccount.setUsername(memberAccountVO.getUsername());
		memberAccount.setPassword(md5Password);
		memberAccount.setSalt(salt);
		memberAccount.setCreate_by(memberAccountVO.getUsername());
		memberAccount.setUpdate_by(memberAccountVO.getUsername());
		Integer id = memberAccountDao.insert(memberAccount);
		if(id == 0) return Optional.of("新增会员帐号时发生错误");

		// 新增Member 资料
		Member member = new Member();
		member.setMa_id(String.valueOf(id));
		member.setName(memberAccountVO.getName());
		member.setCreate_by(memberAccountVO.getUsername());
		member.setUpdate_by(memberAccountVO.getUsername());
		Integer result = memberService.insert(member);
		if(result == 0) return Optional.of("新增会员资料时发生错误");
		
		return Optional.empty();
	}

	@Override
	public MemberAccount findMemberAccountByUsername(String username) {
		// TODO Auto-generated method stub
		return memberAccountDao.findMemberAccountByUsername(username);
	}

}

Github

新增注册与登入功能及其测试方法


<<:  IOT 组别

>>:  Day15 Gin's Router And RESTful API

# Day 28 Page Migration (三)

文件 原文文件:Page migration 翻译: Non-LRU 分页迁移 ==========...

Day15 - 产品编辑 modal 还丑但功能 OK 了

<template> <!-- Button trigger modal --&...

【RPA介绍】如何用UiPath Studio把重复性流程自动跑起来!

一、RPA是什麽? RPA 是 Robotic Process Automation的缩写,简称机器...

Rust-枚举(enumeration)

枚举就是列出有穷序列的型别 透过enum关键字新增了新的Browser型别在范例中列出了一个组项分别...

Day 10 : 案例分享(3.3) 会计模组-调节、立冲帐、应收付与收支款

案例说明及适用场景 当我们有某一个科目,需要管理他是否还有余额未被处理,这个科目就是所谓的 调节科目...