开发登录和退出
创建实体类loginTicket
为了实现用户可以在多个请求间,服务器可以记住浏览器的用户信息,创建LoginTicket表,用户登录后,服务器生成一个ticket凭据,同时保存用户的user_id,通过user_id可以进一步查询到用户的详细信息。服务器把ticket凭据用cookie返回给浏览器,浏览器下次请求时就会带上ticket。
user_id 用户的id标识
ticket 凭证 随机字符串 唯一标识
status 状态 0有效 1失效
expired 凭据过期时间
public class LoginTicket {
private int id;
private int userId;
private String ticket;
private int status;
private Date expired;
}
编写对应的LoginTicketMapper
实现:
1、login_ticket表中插入数据,
2、通过ticket查询数据,
3、和更新用户凭据状态三个方法
4、也可以通过注解的形式来实现Mapper对应的sql
5、 @Options(useGeneratedKeys = true, keyProperty = “id”) 指定表中的id为自增长
6、注解中也可以使用if判断,不过要麻烦点
@Mapper
public interface LoginTicketMapper {
@Insert({
"insert into login_ticket(user_id,ticket,status,expired) ",
"values(#{userId},#{ticket},#{status},#{expired})"
})
@Options(useGeneratedKeys = true, keyProperty = "id")
//向login_ticket表中插入数据,
public int insertLoginTicket(LoginTicket loginTicket);
//通过ticket查询数据,
@Select({
"select * from login_ticket ",
"where ticket=#{ticket}"
})
public LoginTicket selectLoginTicket(String ticket);
//和更新用户凭据状态三个方法
@Update({
"<script>",
"update login_ticket set status=#{status} where ticket=#{ticket} ",
"<if test=\"ticket!=null\"> ",
"and 1=1 ",
"</if>",
"</script>"
})
public int updateLoginTicket(String ticket,int status);
}
测试方法是否正确
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class LoginTicketTest {
@Autowired(required = false)
LoginTicketMapper loginTicketMapper;
@Test
//测试插入
public void insertTest()
{
LoginTicket loginTicket=new LoginTicket();
loginTicket.setUserId(1007);
loginTicket.setTicket(CommunityUtil.generateUUID());
loginTicket.setStatus(0);
loginTicket.setExpired(new Date());
loginTicketMapper.insertLoginTicket(loginTicket);
}
@Test
//测试查询
public void selectTest()
{
LoginTicket loginTicket = loginTicketMapper.selectLoginTicket("34a2635b516c46baa7f5accf686d4cd9");
System.out.println(loginTicket);
}
@Test
//测试修改
public void updatetTest()
{
loginTicketMapper.updateLoginTicket("34a2635b516c46baa7f5accf686d4cd9",1);
}
}
编写Service处理登录业务
先判断账号,密码,状态
//登录业务
public Map<String, Object> login(String username, String password, int expiredSeconds) {
Map<String, Object> map = new HashMap<>();
// 空值处理
if (StringUtils.isBlank(username)) {
map.put("usernameMsg", "账号不能为空!");
return map;
}
//密码为空
if(StringUtils.isBlank(password))
{
map.put("passwordMsg","密码不能为空");
return map;
}
// 验证账号是否存在
User user = userMapper.queryByName(username);
if(user==null)
{
map.put("usernameMsg", "该账号不存在!");
return map;
}
// 验证状态
if (user.getStatus() == 0) {
map.put("usernameMsg", "该账号未激活!");
return map;
}
// 验证密码,验证的是用户的密码加上用户加密的随机字符串之后生成的密码是否正确
String s = CommunityUtil.MD5(password + user.getSalt());
if(!s.equals(user.getPassword()))
{
map.put("passwordMsg","密码错误!");
return map;
}
// 生成登录凭证,存放到数据库中的login_ticket表
LoginTicket loginTicket=new LoginTicket();
loginTicket.setUserId(user.getId());
loginTicket.setTicket(CommunityUtil.generateUUID());
loginTicket.setStatus(0);
loginTicket.setExpired(new Date(System.currentTimeMillis() + expiredSeconds * 1000));
loginTicketMapper.insertLoginTicket(loginTicket);
map.put("ticket", loginTicket.getTicket());
return map;
}
编写Controller处理请求
请求路径可以相同 返回方法要不同
-
先比较验证码是否正确 忽略大小写 错误就回到登录页面
-
再检查账号和密码是否正确
-
调用service方法 根据传回的map判断是否登录成功
-
map.containsKey(“ticket”) 为真 说明登录成功,生成了ticket,此时把ticket通过cookie传给浏览器
-
用户是否选择记住我 定义常量默认超时时间
/** * 默认状态的登录凭证的超时时间 */ int DEFAULT_EXPIRED_SECONDS = 3600 * 12; /** * 记住状态的登录凭证超时时间 */ int REMEMBER_EXPIRED_SECONDS = 3600 * 24 * 100;
//处理登录
@RequestMapping(path = "/login", method = RequestMethod.POST)
public String login(String username, String password, String code, boolean rememberme,
Model model, HttpSession session, HttpServletResponse response) {
// 检查验证码
String kaptcha = (String) session.getAttribute("KaptchaCode");
if (StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equalsIgnoreCase(code)) {
model.addAttribute("codeMsg", "验证码不正确!");
return "/site/login";
}
// 检查账号,密码
//设置过期时间
int expiredSeconds = rememberme ? REMEMBER_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS;
//传入参数调用service方法
Map<String, Object> map = userService.login(username, password, expiredSeconds);
//根据传回的map判断是否登录成功
if(map.containsKey("ticket"))
{ //说明登录成功,生成了ticket,此时把ticket通过cookie传给浏览器
Cookie cookie=new Cookie("ticket",map.get("ticket").toString());
cookie.setPath(contextPath);
cookie.setMaxAge(expiredSeconds);
response.addCookie(cookie);
return "redirect:/index";
}else {
//没有说明登录失败返回相应的信息,map.get的值为空说明不是这个错误(用户名或者密码错误)
model.addAttribute("usernameMsg", map.get("usernameMsg"));
model.addAttribute("passwordMsg", map.get("passwordMsg"));
return "/site/login";
}
}
修改页面
从request请求中取值
也可以从model 但需要手动添加基本变量
th:value="${param.username}"
<div class="form-group row">
<label for="username" class="col-sm-2 col-form-label text-right" >账号:</label>
<div class="col-sm-10">
<input type="text" th:class="|form-control ${usernameMsg!=null?'is-invalid':''}|"
th:value="${param.username}"
id="username" name="username" placeholder="请输入您的账号!" required>
<div class="invalid-feedback" th:text="${usernameMsg}">
该账号不存在!
</div>
</div>
</div>
开发退出功能
业务层就是把用户状态改为1即可
//退出
public void logout(String ticket)
{
loginTicketMapper.updateLoginTicket(ticket,1);
}
//处理退出
@RequestMapping(value = "/logout",method = RequestMethod.GET)
public String logout(@CookieValue("ticket") String ticket)
{
userService.logout(ticket);
return "redirect:/login";
}