基本思路
- 登陆时,勾选七天免密登录,将当前对象的Json字符串加密后,保存到客户端的cookie
- 使用过滤器,当路径是登录页面时,判断当前是否有session,如果有,直接登录进index页面
- 如果没有session,判断当前是否有名字为User的cookie,
如果有,进一步判断User在数据库中是否存在
如果存在,创建一个session,进入index页面 - 如果不存在cookie,进入登录页面(loginShow)
代码案例
前端页面【bootstrap的写法】【可以换为普通写法】
<form class="form-horizontal" action="login.do" method="post">
<!--把标签和控件放在一个带有 class .form-group 的 <div> 中。-->
<!--.form-group:使表单元素上下保持间距-->
<div class="form-group">
<!--向标签添加 class .control-label。使文字紧贴在输入框的左边-->
<label for="name" class="col-sm-4 control-label">账号</label>
<div class="col-sm-6">
<!--form-control:输入框的样式-->
<input type="text" class="form-control" id="username" name="username" placeholder="输入账号" value="${username}">
</div>
</div>
<div class="form-group">
<!--向标签添加 class .control-label。使文字紧贴在输入框的左边-->
<label for="name" class="col-sm-4 control-label">密码</label>
<div class="col-sm-6">
<!--form-control:输入框的样式-->
<input type="password" class="form-control" id="password" name="password" placeholder="输入密码">
</div>
</div>
<div class="form-group">
<!--offset 列 sm 偏移量 4 -->
<div class="checkbox col-sm-offset-4">
<label>
<input type="checkbox" id="savePwd" name="savePwd"> 7天免密登录
</label>
</div>
</div>
<div class="form-group">
<!--offset 列 sm 偏移量 4 -->
<div class="col-sm-offset-4">
<input type="submit" class="btn btn-primary" value="登录">
<input type="reset" class="btn btn-primary" value="重置">
<!-- <button type="submit" class="btn btn-primary">添加</button> -->
</div>
</div>
</form>
登录后,创建session,创建cookie存储用户加密后的信息
@RequestMapping("login")//表示user/login.do(有后缀)
public String login(HttpServletRequest request,HttpServletResponse response,User u,boolean savePwd) {
u.setPassword(MD5Utils.getMD5Value(u.getPassword()));//密码加密
UserService userService=new UserServiceImpl();
User user=userService.searchByUser(u);//如果找不到,返回null
if(user!=null) {//登陆成功
//a.登陆成功,在Session存一个user对象--》IndexController
//【session:权限控制,用于判断是否登陆成功,通过过滤器实现】
HttpSession session = request.getSession();//返回request的session,如果没有session就创建一个session
session.setAttribute("user", user);
//session设置失效时间是从什么时候算起?
//从session不活动的时候开始计算,如果session一直活动,session就总不会过期。
//从该Session未被访问,开始计时; 一旦Session被访问,计时清0;
//Session的默认失效时间是30分钟,关闭当前标签(不关闭浏览器),可以再次直接进入任何路径
session.setMaxInactiveInterval(9000);//session存活时间,单位:秒,15分钟
//注意:此时,cookie中保存JSESSIONID的时间是默认的-1,
// 即,关闭浏览器,cookie的JSESSIONID失效
// 所以,再次打开浏览器时,虽然服务器的session仍然有效,但是,cookie的JSESSIONID失效
// =》session是基于cookie的,所以,需要重新登录
//session 并不是随着浏览器的关闭而关闭的
//session只会随着其生命周期结束或者强制销毁
//确保JSESSIONID不随着浏览器的关闭而失效的方法【保证浏览器再次打开,也可以使用当前的cookie】
//CookieUtils.createCookie(response,"JSESSIONID",session.getId(),1);//关闭浏览器,在打开浏览器,也能登陆成功
//但是,浏览器关闭之后,session就应该失效,所以,通常不使用上面的写法【此处,作为尝试】
if(savePwd) {//七天免密登录
//创建cookie记录,name为User,value为当前user的json加密信息,存活时间7天
String userJson=AESUtils.key2Json(user);//将user转变为userJson
//createKey()会自动创建一个key
//Key key=AESUtils.createKey();
Key key=AESUtils.readKey();//获取文件中的key
String encodeUser=AESUtils.encode(userJson,key);//对称加密,userJson加密为encodeUser
CookieUtils.createCookie(response, "User",encodeUser, 7);//保存到cookie
}
//1.创建cookie,并发送到客户端
// 每次登录,都创建当前username对象,并覆盖原来的cookie
// Cookie是可以覆盖的,如果重复写入同名的Cookie,那么将会覆盖之前的Cookie。
CookieUtils.createCookie(response,"username",user.getUsername());
return "redirect:/index.do";/* /index.do 表示:项目工程名//index.do */
}else {
request.setAttribute("msgr", "用户名或密码错误!");
//model.addAttribute("msgr", "用户名或密码错误!");
return "login";//不规范的写法,为了获取cookie中的用户名
}
}
- 如果savePwd时true,表示勾选了,七天免密登录
- AESUtils.key2Json(user):将User对象变为Json字符串
- AESUtils.readKey():获取服务器中的key文件
- AESUtils.encode(userJson,key):对称加密
- CookieUtils.createCookie(response, “User”,encodeUser, 7):创建cookie,保存7天
使用过滤器,对登录页面进行过滤,判断session和cookie
@WebFilter(value= {"/user/showLogin.do"})
public class PwdFreeLoginFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request1 =(HttpServletRequest) request;
HttpServletResponse response1 =(HttpServletResponse) response;
HttpSession session =request1.getSession();
//System.out.println("PwdFreeLoginFilter:"+session.getAttribute("user"));
UserService userService=new UserServiceImpl();
if(session.getAttribute("user")!=null) {//如果session有效,直接进入首页
response1.sendRedirect(request1.getContextPath()+"/index.do");//使用绝对路径:项目名+/user/showLogin.do
}else{//如果session失效
ObjectMapper mapper=new ObjectMapper();
String encodeUser=CookieUtils.getCookie(request1, "User");
if(encodeUser!=null) {//判断cookie是否存在user的id=》User
Key key=AESUtils.readKey();//获取文件中的key
String userJson=AESUtils.decode(encodeUser,key);//对称解密,将encodeUser还原回userJson
User u=AESUtils.json2Key(userJson,User.class);//将userJson还原为user
User user=userService.searchByUser(u);//如果找不到,返回null
if(user!=null) {//登陆成功
//创建session
session.setAttribute("user", user);
session.setMaxInactiveInterval(9000);//session存活时间,单位:秒,15分钟
request.getRequestDispatcher("/WEB-INF/index.jsp").forward(request1, response1);//不正规的写法
}
chain.doFilter(request1, response1);//进入登录页面【进入当前页面】
}else {//cookie不存在用户信息
chain.doFilter(request1, response1);//进入登录页面【进入当前页面】
}
}
}
}
难点备注
-
原本是想将key也存储到cookie中的,但是key转换为JSON之后,反序列化失败
-
查找原因是:Jackson的ObjectMapper类的readValue会调用无参构造方法(不一定准确,网上的说法)
-
我自己在使用的时候,发现Jackson的ObjectMapper类的writeValueAsString会调用setter方法
因为我在密码的set方法内进行了加密,但是writeValueAsString转化对象为Json后,原来已经加密的密码又加密了一次 -
所以,最后使用了ObjectInputStream流进行了反序列化,key密钥文件保存在服务器
注:对称加密工具类在另一篇文章中,需要的请去自取