功能分析:
1、注册时对用户名和密码做一些限制:用户名和密码不能为空,长度限制...用map保存信息并把map作为返回值返回。
2、密码存储的时候,原密码加盐值(盐值可以用UUID随机生成),在通过MD5加密后存进数据库。
3、登录时,账号密码验证成功后,给该用户下发一个ticket(cookie:理解为sessionID),如果点击“记住我”,就设置cookie的生存时间更长,例如5天。
(1) model层:主要是两个类:User 和 LoginTicket :LoginTicket中关联userId
public class User {
private int id;
private String name;
private String password;
private String salt;
private String headUrl;
}
public class LoginTicket {
private int id;
private int userId;
private Date expired;
private int status;// 0有效,1无效
private String ticket;
}
(2)Dao层是普通的CRUD:基于注解实现
但是:这里先把表名,表字段抽取出去,使得系统的比较完善。将来如果需要改变表,只需改变String 中的表名和字段名,无需要更改SQL语句。
public interface UserDAO {
String TABLE_NAME = "user";
String INSET_FIELDS = " name, password, salt, head_url ";
String SELECT_FIELDS = " id, name, password, salt, head_url";
//增加用户
@Insert({"insert into ", TABLE_NAME, "(", INSET_FIELDS,
") values (#{name},#{password},#{salt},#{headUrl})"})
int addUser(User user);
//通过id查询用户
@Select({"select ", SELECT_FIELDS, " from ", TABLE_NAME, " where id=#{id}"})
User selectById(int id);
//通过名字查询用户
@Select({"select ", SELECT_FIELDS, " from ", TABLE_NAME, " where name=#{name}"})
User selectByName(String name);
//删除用户
@Delete({"delete from ", TABLE_NAME, " where id=#{id}"})
void deleteById(int id);
}
public interface LoginTicketDAO {
String TABLE_NAME = "login_ticket";
String INSERT_FIELDS = " user_id, expired, status, ticket ";
String SELECT_FIELDS = " id, " + INSERT_FIELDS;
@Insert({"insert into ", TABLE_NAME, "(", INSERT_FIELDS,
") values (#{userId},#{expired},#{status},#{ticket})"})
int addTicket(LoginTicket ticket);
@Select({"select ", SELECT_FIELDS, " from ", TABLE_NAME, " where ticket=#{ticket}"})
LoginTicket selectByTicket(String ticket);
//更新ticket状态
@Update({"update ", TABLE_NAME, " set status=#{status} where ticket=#{ticket}"})
void updateStatus(@Param("ticket") String ticket, @Param("status") int status);
}
(3) UserService层的方法实现
public Map<String, Object> register(String username, String password) {
Map<String, Object> map = new HashMap<String, Object>();
if (StringUtils.isBlank(username)) {
map.put("msg", "用户名不能为空");
return map;
}
if (StringUtils.isBlank(password)) {
map.put("msg", "密码不能为空");
return map;
}
//查出用户信息
User user = userDAO.selectByName(username);
if (user != null) {
map.put("msg", "用户名已经被注册");
return map;
}
// 密码强度
user = new User();
user.setName(username);
//利用UUID随机生成一个盐值,再通过MD5加密存进数据库
user.setSalt(UUID.randomUUID().toString().substring(0, 5));
//给用户设置头像
String head = String.format("http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000));
user.setHeadUrl(head);
user.setPassword(WendaUtil.MD5(password+user.getSalt()));
//增加用户
userDAO.addUser(user);
// 注册成功后,顺便给用户增加一个登陆sessionID
String ticket = addLoginTicket(user.getId());
map.put("ticket", ticket);
return map;
}
public Map<String, Object> login(String username, String password) {
Map<String, Object> map = new HashMap<String, Object>();
if (StringUtils.isBlank(username)) {
map.put("msg", "用户名不能为空");
return map;
}
if (StringUtils.isBlank(password)) {
map.put("msg", "密码不能为空");
return map;
}
User user = userDAO.selectByName(username);
if (user == null) {
map.put("msg", "用户名不存在");
return map;
}
//把用户输入的密码加上原来的盐值再次加密后与数据库的密码进行匹配
if (!WendaUtil.MD5(password+user.getSalt()).equals(user.getPassword())) {
map.put("msg", "密码不正确");
return map;
}
//登录成功给一个sessionId
String ticket = addLoginTicket(user.getId());
map.put("ticket", ticket);
map.put("userId", user.getId());
return map;
}
private String addLoginTicket(int userId) {
LoginTicket ticket = new LoginTicket();
ticket.setUserId(userId);
Date date = new Date();
date.setTime(date.getTime() + 1000*3600*24);
ticket.setExpired(date);
ticket.setStatus(0);
//随机生成一个字符串作为ticket
ticket.setTicket(UUID.randomUUID().toString().replaceAll("-", ""));
loginTicketDAO.addTicket(ticket);
return ticket.getTicket();
}
(4)Controller层
1、用户从系统首页点击注册按钮:跳转到注册页面,输入注册信息。Controller层调用Service层的方法进行注册,然后拿到Service层的方法的放回值。根据返回值进行请求处理。
@RequestMapping(path = {"/reg/"}, method = {RequestMethod.POST})
public String reg(Model model, @RequestParam("username") String username,
@RequestParam("password") String password,
@RequestParam("next") String next,
@RequestParam(value="rememberme", defaultValue = "false") boolean rememberme,
HttpServletResponse response) {
try {
//调用userService.register(),该方法返回一个map
Map<String, Object> map = userService.register(username, password);
//如果map中包含sessionId,说明注册成功
if (map.containsKey("ticket")) {
//用户注册成功,把sessionID设置进Cookie中
Cookie cookie = new Cookie("ticket", map.get("ticket").toString());
cookie.setPath("/");
//如果用户点击记住我,则延长sessionID存活时间
if (rememberme) {
cookie.setMaxAge(3600*24*5);
}
response.addCookie(cookie);
if (StringUtils.isNotBlank(next)) {
return "redirect:" + next;
}
//返回主页
return "redirect:/";
//否则,注册失败
} else {
//把失败信息存在model,返回给页面,并且返回登录页面
model.addAttribute("msg", map.get("msg"));
return "login";
}
} catch (Exception e) {
//把异常信息存在model,返回给页面,并且返回登录页面
logger.error("注册异常" + e.getMessage());
model.addAttribute("msg", "服务器错误");
return "login";
}
}
2、该用户是注册过的,直接登录,依然是调用services层的login()方法进行判断,以及下发sessionID,成功进入主页,失败返回登录页面。
@RequestMapping(path = {"/login/"}, method = {RequestMethod.POST})
public String login(Model model, @RequestParam("username") String username,
@RequestParam("password") String password,
@RequestParam(value="next", required = false) String next,
@RequestParam(value="rememberme", defaultValue = "false") boolean rememberme,
HttpServletResponse response) {
try {
Map<String, Object> map = userService.login(username, password);
if (map.containsKey("ticket")) {
Cookie cookie = new Cookie("ticket", map.get("ticket").toString());
cookie.setPath("/");
if (rememberme) {
cookie.setMaxAge(3600*24*5);
}
response.addCookie(cookie);
if (StringUtils.isNotBlank(next)) {
return "redirect:" + next;
}
return "redirect:/";
} else {
model.addAttribute("msg", map.get("msg"));
return "login";
}
} catch (Exception e) {
logger.error("登陆异常" + e.getMessage());
return "login";
}
}
注意点:
1、为了保证用户信息安全,用户密码必须加密存储。
2、设置了一个Ticket用户session共享。
3、必要的字段参数判断。