本文共 8565 字,大约阅读时间需要 28 分钟。
user登录的属性
id是主键 ticket凭证 status登录状态0有效1无效 expired过期时间 ticket由服务器发给浏览器,作为cookie保存在浏览器,其他信息在服务端保存。下次服务端收到了cookie识别出是来自哪个浏览器,并可取出其他信息。写程序的顺序:数据访问层->业务->表现层
5个属性id、userId、ticket、status、expired,getset方法,重写toString
public class LoginTicket { private int id; private int userId; private String ticket;//凭证字符串 private int status;//状态0有效1无效 private Date expired;//到期日期 public int getId() { return id; } public void setId(int id) { this.id = id; } public int getUserId() { return userId; } public void setUserId(int userId) { this.userId = userId; } public String getTicket() { return ticket; } public void setTicket(String ticket) { this.ticket = ticket; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public Date getExpired() { return expired; } public void setExpired(Date expired) { this.expired = expired; } @Override public String toString() { return "LoginTicket{" + "id=" + id + ", userId=" + userId + ", ticket='" + ticket + '\'' + ", status=" + status + ", expired=" + expired + '}'; }}
三个方法:插入、查询、退出
@Mapperpublic interface LoginTicketMapper { //实现sql语句:可以在xml里写,也可以通过注解,字符串拼接成,书写方便,每行字符串加一个空格,主键自动生成 @Insert({ "insert into login_ticket(user_id,ticket,status,expired) ", "values(#{userId},#{ticket},#{status},#{expired})" }) @Options(useGeneratedKeys = true, keyProperty = "id")//希望主键id是自动生成的,生成的值注入给对象,指定属性id int insertLoginTicket(LoginTicket loginTicket);//插入输出,影响行数 @Select({ "select id,user_id,ticket,status,expired ", "from login_ticket where ticket=#{ticket}" }) LoginTicket selectByTicket(String ticket);//查询,依据ticket,ticket是凭证,发送给cookie浏览器存,其他的在服务器存,下次cookie给服务器,服务器查到其他的数据,ticket唯一标识 //退出,状态改变 @Update({ "" })//动态sql,if怎么用?
测试sql是否写正确了,在MapperTests写测试,先@Autowired注入loginTicketMapper:
@Test public void testInsertLogin() { LoginTicket loginTicket = new LoginTicket(); loginTicket.setUserId(101); loginTicket.setTicket("abc"); loginTicket.setStatus(0); loginTicket.setExpired(new Date((System.currentTimeMillis() + 1000 * 60 *10))); loginTicketMapper.insertLoginTicket(loginTicket); } @Test public void testSelectLoginTicket() { LoginTicket loginTicket = loginTicketMapper.selectByTicket("abc"); System.out.println(loginTicket); loginTicketMapper.updateStatus("abc", 1); loginTicket = loginTicketMapper.selectByTicket("abc"); System.out.println(loginTicket); }
控制台打印:结果正确
注入mapper
输入:用户名、密码、多久后失效 输出:Map<String, Object>,用Map装多个情况,登录失败的原因(账号没输入、没存在、没激活),登录成功的凭证 处理逻辑:补充在UserService.java的登录代码:
public Maplogin(String username, String password, int expiredSeconds) { Map map = new HashMap<>(); // 空值处理 if (StringUtils.isBlank(username)) { map.put("usernameMsg", "账号不能为空!"); return map; } if (StringUtils.isBlank(password)) { map.put("passwordMsg", "密码不能为空!"); return map; } // 验证账号 User user = userMapper.selectByName(username); if (user == null) { map.put("usernameMsg", "该账号不存在!"); return map; } // 验证状态 if (user.getStatus() == 0) { map.put("usernameMsg", "该账号未激活!"); return map; } // 验证密码 password = CommunityUtil.md5(password + user.getSalt()); if (!user.getPassword().equals(password)) { map.put("passwordMsg", "密码不正确!"); return map; } // 生成登录凭证 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; }
收到表单(POST)给UserService处理,登录成功回首页、登录失败回登录页面.
补充在LoginController.java访问路径相同时,method的方法不同也能区分开,一个是GET,一个是POST.
@RequestMapping(path = "/login", method = RequestMethod.POST)
表单传入条件:账号username 密码password 验证码code “记住我”rememberme;返回数据、响应时需要model,页面传的验证码code要和服务器生成的的验证码(在session里)比较,要从session中取出来需要HttpSession session
,登录成功要把ticket发给客户端让它保存,用cookie保存需要HttpServletResponse response
public String login(String username, String password, String code, boolean rememberme, Model model, HttpSession session, HttpServletResponse response) {
先检查验证码(从session里get),为空或不匹配,返回登录页面
// 检查验证码 String kaptcha = (String) session.getAttribute("kaptcha"); if (StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equalsIgnoreCase(code)) { model.addAttribute("codeMsg", "验证码不正确!"); return "/site/login"; }
调用userService的login方法,返回map
int expiredSeconds = rememberme ? REMEMBER_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS; Mapmap = userService.login(username, password, expiredSeconds);
含有ticket凭证,给ticket创建cookie,设置路径,最长登录时间,加到responsed对象里,响应时发送给浏览器,登录成功返回首页。
if (map.containsKey("ticket")) { Cookie cookie = new Cookie("ticket", map.get("ticket").toString()); cookie.setPath(contextPath); cookie.setMaxAge(expiredSeconds); response.addCookie(cookie); return "redirect:/index";
不含有ticket凭证,返回账号、密码的错误信息,从map里面get。map。get返回null也不会有影响,不是usernameMsg有问题,就是passwordMsg有问题。
} else { model.addAttribute("usernameMsg", map.get("usernameMsg")); model.addAttribute("passwordMsg", map.get("passwordMsg")); return "/site/login"; }
一些细节处理:
public class LoginController implements CommunityConstant
/** * 默认状态的登录凭证的超时时间 */ int DEFAULT_EXPIRED_SECONDS = 3600 * 12; /** * 记住状态的登录凭证超时时间 */ int REMEMBER_EXPIRED_SECONDS = 3600 * 24 * 100;
server.servlet.context-path=/community
,即所有路径都在/community下,可以通过注解注入或repueat对象获得,这里用注解注入方式@Value()
,将变量server.servlet.context-path
给到属性contextPath
.@Value("${server.servlet.context-path}") private String contextPath;
每一个框上加name="username"
,name="password"
,name="code"
和LoginController中login的方法的参数要一致。
th:value="${param,username}"
,th:value="${param.password}"
,th:checked="${param.rememberme}"
规则:如果参数是实体,这里是user,Spring MVC会把user放到model里,页面上可以直接得到user对象的数据(这是从model里面获得的);如果是普通参数,如基本类型,字符串,不会把参数放到model里,可以认为放到model里,但是是存在request对象里的,可以通过request.getParameter获得,因为是请求里携带过来的,当程序执行到html文件时,可以从request里面取值。 thymleaf里语法是th:value="${param,username}"
,th:value="${param.password}"
每次验证码要刷新,所以不用给它默认值 “记住我”出错,√应该还在,是通过check设置的,动态判断th:checked="${param.rememberme}"
,从request取参数。 其他错误提示:账号相关提示,动态
该账号不存在!
密码长度不能小于8位!
验证码不正确!
是否显示该样式’is-invalid’:如果确实有问题Msg才显示这个样式’is-invalid’,否则为空。class不应该写死,是固定样式+动态样式。
写过了,这里再看一下
//退出,状态改变 @Update({ "
public void logout(String ticket) { loginTicketMapper.updateStatus(ticket, 1); }
@RequestMapping(path = "/logout", method = RequestMethod.GET) public String logout(@CookieValue("ticket") String ticket) { userService.logout(ticket); return "redirect:/login"; }
修改index.html中退出登录按键,改退出路径th:href=:@{/logout}
先检查验证码是否正确,验证码正确后才检查账号,密码。
在浏览器登录后,开检查选项,在Application下选择cookies选项,看到对应的ticket
status是是否登录状态,为1的是退出后的(不会删除数据),0是当前登录的。注:html中,@{}路径 ${}变量 #{}取id
转载地址:http://ysnlf.baihongyu.com/