概要
通过servlet、javaWeb、Cookie实现用户的登录、注册、记住用户名登录、30天内免密自动登录、
退出登录等功能。通过JDBC规范连接数据库、操作MySQL数据库,实现用户信息的录入、删除、修改、查询。
逻辑:
登陆时候通过JDBC操作数据库,并且jsp前端会提示用户名密码等不能为空,通过ajax实现用户名数据库中是否已经存在、是否可用。记住用户名登录、30天内自动登录是通过勾选选项进行登录的时候生成Cookie,后续对CooKie是否存在进行判断从而实现是否执行操作、用户信息在前端显示是通过session域进行数据共享。过滤器实现自动登录,当检测到自动登录cookie存在时,过滤器实现自动登录。当退出登录的时候,把存储着用户信息的session清除、同时把自动的Cookie也删除。
效果展示:注册:
记住用户名登录:
30天内免密登录:
关掉浏览器再次访问界面:自动登录了
登录功能
首先登录的jsp代码如下:
<%@ page import="java.net.URLEncoder" %>
<%@ page import="java.net.URLDecoder" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="<%=request.getContextPath()%>/css/login.css"/>
<title>EasyMall欢迎您登陆</title>
</head>
<%
Cookie[]cs=request.getCookies();
Cookie findC=null;
if (cs!=null){
for (Cookie c:cs
) {
if("remname".equals(c.getName())){
findC=c;
break;
}
}
}
String username="";
if(findC!=null){
username= URLDecoder.decode(findC.getValue(),"utf-8");
}
%>
<body>
<h1>欢迎登陆EasyMall</h1>
<form action="<%=request.getContextPath()%>/servlet/LoginServlet" method="POST">
<table>
<tr>
<td colspan="2" style="color: red;text-align: center">
<%=request.getAttribute("msg")==null?"":request.getAttribute("msg")%>
</td>
</tr>
<tr>
<td class="tdx">用户名:</td>
<td><input type="text" name="username" id="username" value="<%=username%>"/></td>
</tr>
<tr>
<td class="tdx">密码:</td>
<td><input type="password" name="password" id="password"/></td>
</tr>
<tr>
<td colspan="2"><!-- 如果获取到的指定Cookie不为空,说明这个用户名上次记住了,这次也默认勾选记住-->
<input type="checkbox" name="remname" value="true"
<%if (findC!=null){%>
checked="checked"
<%}
%>
/>记住用户名
<input type="checkbox" name="autologin" value="true"/>30天内自动登陆
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="登陆"/>
</td>
</tr>
</table>
</form>
</body>
</html>
request.getContextPath()主要是为了解决相对路径的问题,可以返回站点的根路径
封装JDBC规范来操作数据库:
public class JDBCUtiles {
//创建数据源对象
private static DataSource dataSource=new ComboPooledDataSource();
//从数据源对象中获取连接
public static Connection getConn() {
try {
return dataSource.getConnection();
} catch (SQLException throwables) {
throwables.printStackTrace();
throw new RuntimeException(throwables);
}
}
//关闭连接
public static void close(Connection conn, Statement stat, ResultSet rs){
if (rs != null) {
try {
rs.close(); //将连接还给数据源
} catch (SQLException throwables) {
throwables.printStackTrace();
}
finally {
rs=null;
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
finally {
conn=null;
}
}
if(stat!=null){
try {
stat.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
finally{
stat=null;
}
}
}
}
封装好登录的接口,方便后面的30天内自动登录
public class LoginService { public User Login(String username, String password) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; User user=new User(); try { conn = JDBCUtiles.getConn(); ps = conn.prepareStatement("select * from user where username=?"); ps.setString(1, username); rs = ps.executeQuery(); if (rs.next()) { if (rs.getString(3).equals(password)) { user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); user.setPassword(rs.getString("password")); user.setNickname(rs.getString("nickname")); user.setEmail(rs.getString("email")); } } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JDBCUtiles.close(conn,ps,rs); } return user; } }
通过JDBC操作数据库的步骤:
1.通过封装好的JDBC工具类创建连接对象
2.通过连接对象创建sql语句执行对象statement(预编译对象PrepareStatement)
两者的区别是statemen容易导致sql注入、prepareStatement性能更高
3.创建结果集对象
它们之间的关系:Connetion连接对象相当于一个桥梁在java与数据库之间建立一个联系、连接
Preparestatement预编译对象相当于一个送出小车把sql操作送到数据库中执行
ResultSet结果集对象相当于一个送回小车把数据库执行sql语句的结果运送回来
登录功能的实现:
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
String username=req.getParameter("username");
String password=req.getParameter("password");
//处理记住用户名逻辑
if("true".equals(req.getParameter("remname"))){
Cookie remnamec=new Cookie("remname", URLEncoder.encode(username,"utf-8"));
//设置Cookie保存用户名的时限
remnamec.setMaxAge(60*60*24*30);
//设置Cookie显示的路径,显示在login页面中,不设置就在每个页面都可以显示,不安全
remnamec.setPath(req.getContextPath()+"/login.jsp");
//将cookie回显过去
resp.addCookie(remnamec);
}else {
//没有选择记住用户名,就删除这个响应头为remname的Cookie
Cookie remnamec=new Cookie("remname","");
remnamec.setMaxAge(0);
remnamec.setPath(req.getContextPath()+"/login.jsp");
resp.addCookie(remnamec);
}
LoginService lg=new LoginService();
User user=new User();
user=lg.Login(username,password);
if(user!=null){
//30天内自动登录
if("true".equals(req.getParameter("autologin"))){
String nc=username;
String pc=password;
Cookie nc1=new Cookie("nc", URLEncoder.encode(nc,"utf-8"));
Cookie pc1=new Cookie("pc",URLEncoder.encode(pc,"utf-8"));
nc1.setMaxAge(60*60*24*30);
pc1.setMaxAge(60*60*24*30);
//设置Cookie可以被访问的路径
nc1.setPath(req.getContextPath());
pc1.setPath(req.getContextPath());
resp.addCookie(nc1);
resp.addCookie(pc1);
}
else {
Cookie nc1=new Cookie("nc", "");
Cookie pc1=new Cookie("pc","");
nc1.setMaxAge(60*60*24*30);
pc1.setMaxAge(60*60*24*30);
nc1.setPath(req.getContextPath());
pc1.setPath(req.getContextPath());
resp.addCookie(nc1);
resp.addCookie(pc1);
}
//登录通过session对象保存登录信息,session域可以共享这个数据
req.getSession().setAttribute("user",user);
//用户名密码正确,登录用户,回到主页
resp.sendRedirect(req.getContextPath()+"/index.jsp");
}
else {
req.setAttribute("msg","用户或密码输入错误");
req.getRequestDispatcher("/login.jsp").forward(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
注册界面的jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE HTML>
<html>
<head>
<title>欢迎注册EasyMall</title>
<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="<%=request.getContextPath()%>/css/regist.css"/>
<!--引用js-->
<script type="application/javascript" src="<%=request.getContextPath()%>/js/jquery-1.4.2.js"></script>
<script type="application/javascript">
function setMsg(id,msg,color="red") {
document.getElementById(id+"_msg").innerText=msg;
document.getElementById(id+"_msg").style.color=color;
}
function checkNull(name,msg) {
//清空每一次的错误消息
setMsg(name,"")
var v=document.getElementsByName(name)[0].value;
if(v == null || v == ""){
setMsg(name,msg)
return false;//走到这里面就说明有空的,就返回false
}
return true;//进不去if就说明没有问题,返回true
}
function checkForm() {
var canSub=true;
canSub=checkNull("username","用户名不能为空") && canSub;
canSub=checkNull("password","密码不能为空")&& canSub;
canSub=checkNull("password2","确认密码不能为空")&& canSub;
canSub=checkNull("nickname","昵称不能为空")&& canSub;
canSub=checkNull("email","邮箱不能为空")&& canSub;
canSub=checkNull("valistr","验证码不能为空")&& canSub;
//拿到用户名输入的值
// var username=document.getElementsByName("username")[0].value;
// if(username==null || username==""){
// //alert("用户名不能为空!")
// //document.getElementById("username_msg").innerText="用户名不能为空";
// setMsg("username","用户名不能为空")
// }
// //校验密码不能为空
// var password=document.getElementsByName("password")[0].value;
// if(password==null || password==""){
// setMsg("password","密码不能为空")
// }
//密码一致校验
var psw1=document.getElementsByName("password")[0].value;
var psw2=document.getElementsByName("password2")[0].value;
if(psw1!=null && psw1!="" &&psw2!=null && psw2!="" && psw1!=psw2){
setMsg("password2","两次,密码不一致")
canSub=false;
}
//邮箱格式校验
var email=document.getElementsByName("email")[0].value;
if(email!=null && email!="" && !/^\w+@\w+(\.\w+)+$/.test(email)){
setMsg("email","邮箱格式不正确")
canSub=false;
}
return canSub;
}
//鼠标失去焦点的密码不一致的校验
function checkPassword2() {
setMsg("password2","")
var psw1=document.getElementsByName("password")[0].value;
var psw2=document.getElementsByName("password2")[0].value;
if(psw2==null || psw2==""){
setMsg("password2","确认密码不能为空")
return;
}
if(psw1!=null && psw1!="" &&psw2!=null && psw2!="" && psw1!=psw2){
setMsg("password2","两次,密码不一致")
}
}
//失去焦点邮箱格式的校验
function checkEmail() {
setMsg("email","")
var email=document.getElementsByName("email")[0].value;
if(email==null || email==""){
setMsg("email","邮箱不能为空")
return;
}
if(email!=null && email!="" && !/^\w+@\w+(\.\w+)+$/.test(email)) {
setMsg("email", "邮箱格式不正确")
}
}
/**
* 因为浏览器具有基于url建立的缓存机制
* 当浏览器向相同的链接发送请求时,浏览器的缓存机制就会获取之前访问的相同URL时保留下来的缓存资源
* 为了确保每次请求都是一个不同于之前的请求
* 在请求时,加上时间戳,能避免浏览器对URL的缓存,会使每次请求的URL都不一样,便不会从缓存中读取数据
* @param imgObj
*/
function changeImg(imgObj){
imgObj.src="<%=request.getContextPath()%>/servlet/VerifyCodeServlet?time="+new Date().getTime();
}
//失去焦点校验用户名是否为空 不为空在校验是否存在
function checkUsername() {
var isNotNull=checkNull('username','用户名不能为空');
if(isNotNull){//不为空进来
//不为空在校验是否存在
var username=document.getElementsByName("username")[0].value;
jQuery.get("<%=request.getContextPath()%>/servlet/AjaxHasUserNameServlet",{"username":username},function (data){
setMsg("username",data,data=="用户名可用"?"green":"red")
});
}
}
</script>
</head>
<body>
<h1>欢迎注册EasyMall</h1>
<form action="servlet/RegistServlet" method="POST" onsubmit="return checkForm()">
<table>
<tr>
<td colspan="2" style="color: red;text-align: center">
<%= request.getAttribute("msg")==null ? "" : request.getAttribute("msg")%>
</td>
</tr>
<tr>
<td class="tds">用户名:</td>
<td>
<input type="text" name="username" onblur="checkUsername()" value="<%=request.getParameter("username")==null?"":request.getParameter("username")%>">
<span style="color: red" id="username_msg"></span>
</td>
</tr>
<tr>
<td class="tds">密码:</td>
<td>
<input type="password" name="password" onblur="checkNull('password','密码不能为空')">
<span style="color: red" id="password_msg"></span>
</td>
</tr>
<tr>
<td class="tds">确认密码:</td>
<td>
<input type="password" name="password2" onblur="checkPassword2()">
<span style="color: red" id="password2_msg"></span>
</td>
</tr>
<tr>
<td class="tds">昵称:</td>
<td>
<input type="text" name="nickname" value="<%=request.getParameter("nickname")==null?"":request.getParameter("nickname")%>">
<span style="color: red" id="nickname_msg"></span>
</td>
</tr>
<tr>
<td class="tds">邮箱:</td>
<td>
<input type="text" name="email" onblur="checkEmail()" value="<%=request.getParameter("email")==null?"":request.getParameter("email")%>">
<span style="color: red" id="email_msg"></span>
</td>
</tr>
<tr>
<td class="tds">验证码:</td>
<td>
<input type="text" name="valistr"><img id="yzm_img" src="<%=request.getContextPath()%>/servlet/VerifyCodeServlet" style="cursor: pointer" onclick="changeImg(this)">
<span style="color: red" id="valistr_msg"></span>
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="注册用户"/>
</td>
</tr>
</table>
</form>
</body>
</html>
注册的servlet:
public class RegistServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//解决乱码 1.请求乱码 2.响应乱码
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
//1.获取表单提交的数据
String username=request.getParameter("username");
String password=request.getParameter("password");
String password2=request.getParameter("password2");
String nickname=request.getParameter("nickname");
String email=request.getParameter("email");
String valistr=request.getParameter("valistr");
//2校验用户提交的信息
//TODO验证码的开发
//从session中取到
HttpSession session=request.getSession();
String valistr2=(String) session.getAttribute("valistr");
if(valistr==null||valistr2==null||"".equals(valistr)||"".equals(valistr2)||!valistr.equals(valistr2)){
request.setAttribute("msg","验证码输入有误");
request.getRequestDispatcher("/regist.jsp").forward(request,response);
return;
}
else {
request.getSession().removeAttribute("valistr");
}
//非空校验
if (username==null||"".equals(username)){
//throw new RunnimeException("用户名不能为空");
request.setAttribute("msg","用户名不能为空");
request.getRequestDispatcher("/regist.jsp").forward(request,response);
return;
}
if(password==null||"".equals(password)){
request.setAttribute("msg","密码不能为空");
request.getRequestDispatcher("/regist.jsp").forward(request,response);
return;
}
if(password2==null||"".equals(password2)){
request.setAttribute("msg","确认密码不能为空");
request.getRequestDispatcher("/regist.jsp").forward(request,response);
return;
}
if(nickname==null||"".equals(nickname)){
request.setAttribute("msg","昵称不能为空");
request.getRequestDispatcher("/regist.jsp").forward(request,response);
return;
}
if(email==null||"".equals(email)){
request.setAttribute("msg","邮箱不能为空");
request.getRequestDispatcher("/regist.jsp").forward(request,response);
return;
}
if(!password.equals(password2)){
request.setAttribute("msg","两次密码输入不一致");
request.getRequestDispatcher("/regist.jsp").forward(request,response);
return;
}
if(!email.matches("^\\w+@\\w+(\\.\\w+)+$")){
request.setAttribute("msg","邮箱格式不正确");
request.getRequestDispatcher("/regist.jsp").forward(request,response);
return;
}
if(valistr==null||"".equals(valistr)){
request.setAttribute("msg","验证码不能为空");
request.getRequestDispatcher("/regist.jsp").forward(request,response);
return;
}
//3检查用户名是否存在
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn= JDBCUtiles.getConn();
ps=conn.prepareStatement("SELECT * FROM `user` where username=? ");
ps.setString(1,username);
rs=ps.executeQuery();
if(rs.next()){
request.setAttribute("msg","该用户已经存在");
request.getRequestDispatcher("/regist.jsp").forward(request,response);
return;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
finally {
JDBCUtiles.close(conn,ps,rs);
}
//4注册用户到数据库
conn=null;
ps=null;
try {
conn=JDBCUtiles.getConn();
ps=conn.prepareStatement("INSERT into user VALUES(null,?,?,?,?)");
ps.setString(1,username);
ps.setString(2,password);
ps.setString(3,nickname);
ps.setString(4,email);
ps.executeUpdate();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
finally {
JDBCUtiles.close(conn,ps,null);
}
//5回到主页
// request.getRequestDispatcher("/index.jsp").forward(request,response);
response.getWriter().write("恭喜注册成功,3秒后跳转首页");
response.setHeader("refresh","3;url="+request.getContextPath()+"/index.jsp");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
}
退出登录的Servlet:
/**
* 会话技术
* 浏览器和服务器之间实现某个功能,产生了多次的请求和响应,从第一次请求开始到最后一次请求结束,这期间的
* 这些请求和响应加在一起就称之为产生了一次会话.会话最重要的是如何存储会话之间的数据
* session和cookie
* session:
* session是javaweb提供的解决会话数据存储相关的技术
* session是服务器端的技术,将会话产生的数据存储在服务端
* session技术在服务器为每一个客户创建各自的session对象,用来存储与该客户端会话产生的数据
* 每个客户端都使用各自对应的session对象存储会话数据,不会产生混乱
* */
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//杀死session
//通过杀死session从而使session中存储的登录用户信息删除
req.getSession().invalidate();
//如果退出登录了则删除自动登录的Cookie
Cookie []cookies=req.getCookies();
for (Cookie c:cookies
) {
if("nc".equals(c.getName())){
c.setMaxAge(0);
c.setPath(req.getContextPath());
resp.addCookie(c);
}
if("pc".equals(c.getName())){
c.setMaxAge(0);
c.setPath(req.getContextPath());
resp.addCookie(c);
}
}
//杀死sessioon后,也就是退出登录了,然后重定向到首页
resp.sendRedirect(req.getContextPath()+"/index.jsp");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
记住用户名的servlet:
/**
* * ajax
* * 同步异步
* * 同步:指当浏览器访问一个新的地址,在响应之前阻塞当前页面,知道响应回来后,阻塞结束展示结果
* * 异步:指当浏览器访问一个新的地址,浏览器不会被阻塞,继续工作,当响应回来后回调处理结果
* * 特点:异步请求,局部刷新
* * 应用场景:百度搜索框 直播弹幕
* */
public class AjaxHasUserNameServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//解决乱码
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
//获取请求参数
String username = request.getParameter("username");
System.out.println(username);
//检查用户名是否存在
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn= JDBCUtiles.getConn();
ps=conn.prepareStatement("select * from user where username=?;");
ps.setString(1,username);
rs = ps.executeQuery();
if(rs.next()){
//输出结果
response.getWriter().write("用户名已存在");
}else {
response.getWriter().write("用户名可用");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtiles.close(conn,ps,rs);
}
//返回结果
//response.getWriter().write("ok");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
}
注册时验证码的刷新、显示:
public class VerifyCodeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//禁止浏览器缓存
resp.setIntHeader("Expires",-1);//-1代表禁止缓存
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma","no-cache");
//生成验证码
VerifyCode vc=new VerifyCode();
//输出验证码放到响应中
vc.drawImage(resp.getOutputStream());
//打印验证码
String code = vc.getCode();
System.out.println(code);
//把验证码存储在session中
HttpSession session=req.getSession();
session.setAttribute("valistr",code);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
设置servlet的过滤器,在访问、发起请求的时候,检查是否是30天内自动登录,是的话主页显示用户名、不用再登录
public class AutoFilter implements Filter {
private String excludedPage;
private String[] excludedPages;
@Override //初始化的时候执行一次
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request1=(HttpServletRequest) servletRequest;
HttpServletResponse response1=(HttpServletResponse) servletResponse;
request1.setCharacterEncoding("utf-8");
response1.setContentType("text/html;charset=utf-8");
//获取网页中的所有Cookie
Cookie []cookies= request1.getCookies();
if(cookies!=null){
String uname=null;
String upwd=null;
for (Cookie c:cookies
) {
if("nc".equals(c.getName())){
uname=c.getValue();
}
if("pc".equals(c.getName())){
upwd=c.getValue();
}
}
if (uname!=null&&upwd!=null){
uname= URLDecoder.decode(uname,"utf-8");
upwd= URLDecoder.decode(upwd,"utf-8");
LoginService lg=new LoginService();
User user=new User();
user=lg.Login(uname,upwd);
if (user!=null){
System.out.println("aaa");
request1.getSession().setAttribute("user",user);
}
}
}
filterChain.doFilter(request1,response1);
}
@Override
public void destroy() {
}
}
头部jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE HTML>
<link rel="stylesheet" href="<%=request.getContextPath()%>/css/head.css"/>
<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
<div id="common_head">
<div id="line1">
<div id="content">
<% if (session.getAttribute("user")==null){
%>
<a href="<%=request.getContextPath()%>/login.jsp">登录</a> | <a href="<%=request.getContextPath()%>/regist.jsp">注册</a>
<%
}else {
%>
欢迎回来,<%=((User)session.getAttribute("user")).getUsername()%>!
<a href="<%=request.getContextPath()%>/servlet/LogoutServlet">[登出]</a>
<%}%>
</div>
</div>
<div id="line2">
<img id="logo" src="<%=request.getContextPath()%>/img/head/logo.jpg"/>
<input type="text" name=""/>
<input type="button" value="搜索"/>
<span id="goto">
<a id="goto_order" href="#">我的订单</a>
<a id="goto_cart" href="#">我的购物车</a>
</span>
<img id="erwm" src="<%=request.getContextPath()%>/img/head/qr.jpg"/>
</div>
<div id="line3">
<div id="content">
<ul>
<li><a href="#">首页</a></li>
<li><a href="#">全部商品</a></li>
<li><a href="#">手机数码</a></li>
<li><a href="#">电脑平板</a></li>
<li><a href="#">家用电器</a></li>
<li><a href="#">汽车用品</a></li>
<li><a href="#">食品饮料</a></li>
<li><a href="#">图书杂志</a></li>
<li><a href="#">服装服饰</a></li>
<li><a href="#">理财产品</a></li>
</ul>
</div>
</div>
</div>