BookStore项目【注册和登录】

项目工程框架:

一:建立各个所需的包。

  1. dao       操作数据库,
public User findUserByActiveCode(String activeCode) throws SQLException {
		QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
		return qr.query("select * from user where activecode=?",
				new BeanHandler<User>(User.class), activeCode);
	}
  1. domain     javabean,对象实体类
  2. service    调用dao层对用户的增删改查并处理一些逻辑。所以在dao层只做操作数据库的事。
  3. factory    用来创建service,解耦,使用service更灵活,配合配置文件使用。有点像dubble
  4. web   一些servelt处理请求和响应。 调用service使用(验证码居然也包括在里面)
  5. filter  拦截器,目前主要用于拦截请求,并做编码操作
  6. util  工具包,C3P0Util(自己写的类),发送邮件啊,之类的
  7. exception (可以自定义异常)

 二:注册

注册的页面实现准备好。

二话不说上来就是直接配置拦截器,把编码处理一下。拦截类是有,直接用。在web.xml中配置一下拦截器。

<filter>
		<filter-name>EncodingFilter</filter-name>
		<filter-class>com.itheima.product.web.filter.EncodingFilter</filter-class>
	</filter>

	<filter-mapping>
		<filter-name>EncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

OK,以后就再也不用考虑编码的问题了。

观察注册表的值。  表单提交时<form action="${pageContext.request.contextPath}/register" method="post">

咱们表单提交的地址/register,所以写的servel的地址也应该在web.xml中注册一下。

<servlet>
		<servlet-name>RegisterServlet</servlet-name>
		<servlet-class>com.itheima.product.web.servlet.RegisterServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>RegisterServlet</servlet-name>
		<url-pattern>/register</url-pattern>
	</servlet-mapping>

所以开始写一个RegisterServlet.

RegisterServlet :

public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//处理验证码
		String ckcode = request.getParameter("ckcode");
		String checkcode_session = (String) request.getSession().getAttribute("checkcode_session");
		if(!checkcode_session.equals(ckcode)){//如果两个验证码不一致,跳回注册面
			request.setAttribute("ckcode_msg", "验证码错误!");
			request.getRequestDispatcher("/register.jsp").forward(request, response);
			return ;
		}
		//获取表单数据
		User user = new User();
		try {
			BeanUtils.populate(user, request.getParameterMap());
			user.setActiveCode(UUID.randomUUID().toString());//手动设置激活码
			//调用业务逻辑
			UserService us = new UserService();
			us.regist(user);
			//分发转向
			//要求用户激活后才能登录,所以不能把用户信息保存session中
			//request.getSession().setAttribute("user", user);//把用户信息封装到session对象中
			request.getRequestDispatcher("/registersuccess.jsp").forward(request, response);
		}catch(UserException e){
			request.setAttribute("user_msg", e.getMessage());
			request.getRequestDispatcher("/register.jsp").forward(request, response);
			return;
		}catch (Exception e) {
			e.printStackTrace();
			
		}
		
	}

咱们这个servlet,是用来把新注册的用户写入到数据库的。但是写入之前应该匹配一下验证码。

说到验证码:专门有个servlet生成,因此也要在web.xml中注册一下地址。然后我们在前端,把需要验证码的位置写上请求servelt的地址就好了。(注意哦,生成的验证码是存在session域中的。

request.getSession().setAttribute("checkcode_session", word);)差点忘了,我这个验证码是需要读取txt文件的

    String path = getServletContext().getRealPath("/WEB-INF/new_words.txt");放在WEB-INF下面。

<tr>
    <td style="text-align:right;width:20%;">&nbsp;</td>
		<td colspan="2" style="width:50%"><img                     
          src="${pageContext.request.contextPath}/imageCode" width="180"
          height="30" class="textinput" style="height:30px;" id="img" />&nbsp;&nbsp;
    <a href="javascript:void(0);" onclick="changeImage()">看不清换一张</a>
								</td>
							</tr>

<script type="text/javascript">
	function changeImage() {

		document.getElementById("img").src = "${pageContext.request.contextPath}/imageCode?time="+ new Date().getTime();
	}
</script>

如上,请求图片地址就是servlet地址。然后‘’看不清换一张‘’是一个onclick,这个方法也很简单啦,就是在请求一次,多带了time参数过去了,为的是刷新图片。

OK,验证码完结了。

继续讲咱们的RegisterServlet

那么提交表单后,咱们当然不能让她们直接插入数据了。通过request.getparameter(“activecode”)拿到表单写的验证码,

然后与从session域中取出验证码进行比对.如果错误,将验证码错误存在request域中,然后转发到当前页面,重新来。

request.setAttribute("ckcode_msg", "验证码错误!");

前端通过EL表达式取出来。

<td>${ckcode_msg }</td>

如果正确:那就插入呗。但是插入之前又得注意,咱们发送邮件的激活码还没有生成。因为表单提交中,不会有激活码生成。需要咱们手动生成。user.setActiveCode(UUID.randomUUID().toString());

嗯,然后可以安心插入数据了。之后,将user存入session域中。转发到注册成功的页面。(由于没有激活,所以咱们不让他登录)

Service层:userService:

因为我们需要注册,先写一个regist(user)方法。只需通过调用dao层方法,addUser方法就行了。如果出现异常,手动抛出一个异常(这个我好像没怎么用过,记下。)

抛出: catch (SQLException e) {
            e.printStackTrace();
            throw new UserException("注册失败!");
        }

由于在service层抛出的这个异常,所以在servlet可以捕获这个异常,因为我们调用了service方法啊。

捕获:catch(UserException e){
            request.setAttribute("user_msg", e.getMessage());
            request.getRequestDispatcher("/register.jsp").forward(request, response);
            return;

(小结:1.一般在发生异常,和错误或者不想让程序进行下去时,就用return。如RegisterServlet中的,出现验证码错误

        2.像一般发生异常或者错误啊,就直接将错误存request域中,request.setAttribut.取也很好取,直接${域的名字}.)

Dao层:不愿记,里面涉及到DBUtils使用。用的使用直接拿别人代码改吧

public void addUser(User user) throws SQLException {
		QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
		String sql = "INSERT INTO USER(username,PASSWORD,gender,email,telephone,introduce,activecode,state,registtime) "
				+ "VALUES(?,?,?,?,?,?,?,?,?)";
		qr.update(sql, user.getUsername(), user.getPassword(),
				user.getGender(), user.getEmail(), user.getTelephone(),
				user.getIntroduce(), user.getActiveCode(), user.getState(),
				user.getRegistTime());

	}

三:发送邮件激活码

那么在哪里写激活邮件呢?  在service层中,adduser之后,发送激活邮件。带着邮箱地址,和想写的内容(里面包含激活码,所以说,激活码要在插入用户之前手动插入),发送。

String emailMsg = "注册成功,请<a href='http://www.product.com/activeServlet?activeCode="+user.getActiveCode()+"'>激活</a>后登录";
	
SendJMail.sendMail(user.getEmail(), emailMsg);

那之后呢?他怎么激活的?注意看,咱们给他发送的邮件里的地址,是一个servlet地址,带着参数激活码去的。

那就好办了,写一个servlet,获取到参数。调用service的激活方法activeUser(新建一个)。

service层:activeUser方法首先调用,通过激活码查找用户的方法。首先判断有没有这个用户,有的话然后调用另一个方法将staus状态改变,变为激活状态。

// 修改用户激活状态
	public void activeCode(String activeCode) throws SQLException {
		QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
		qr.update("update user set state=1 where activecode=?", activeCode);

	}

(我发现,那个抛出自定义异常,一般都在service层。不过也只能在这抛了,其他都不允许啊)

四:登录

注册都搞定了,登录就更不是问题了。

又是登录的jsp咯,自己写好。表单提交地址等

新建一个loginservelt,直接取出用户名和密码,调用service方法。service方法调用dao层,写一个通过用户名和密码查出用户的方法。servlet中查出了用户,这里开始注意,用户权限我们是在servlet里面判断的,如果不是管理员,则进入普通页面,是管理员进入另一个页面。这段代码用了path,解决了权限问题。

User user=userService.login(name, password);
			String path="/index.jsp";
			if ("admin".equals(user.getRole())) {
				path="/admin/login/home.jsp";
				
			}
			request.getSession().setAttribute("user",user);
			request.getRequestDispatcher(path).forward(request, response);

然后激活状态呢?我们在service层中判断。就是login方法中。先根据密码和用户名取出user,然后判断staus是否是激活状态,不是,抛出自定义异常。反正servlet接受。e.getMessage(),就是我们在service存入的消息。

catch (UserException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            request.setAttribute("user_msg", e.getMessage());
            request.getRequestDispatcher("/login.jsp").forward(request, response);

(总结:抛出异常总是在service,异常信息也是这时候写入。然后之后servlet捕获异常时,将异常信息存入request域中,转发也是在这时候。也好理解,sevelt中才好处理这些啊,有request,处理转发和存入域中。)

嗯,因为servelt接受抛出自定义异常,总是会带有request域中消息,所以请把登录时错误消息取出来显示到前端页面,EL表达式。

实现30内自动登录和记住用户名:

前端页面:就是多了两个复选框。一个是记住用户名,一个是30天内登录。

<td><input type="checkbox" name="remname" value="true" 
  					<c:if test="${cookie.remname != null }">
  						checked = 'checked'
  					</c:if>
  				/>记住用户名</td>
  				<td><input type="checkbox" name="autologin" value="true" />30天内自动登陆</td>

 你看,记住用户名这是个复选框,并进行了判断。将从cookie中取值,如果有值就选上,没有就默认不选。

为什么非要从cookie中取值呢?因为你退出页面后,在进入还需要有这个勾选中啊,所以只能用cookie,你总不能用其他的域吧

像这种用户退出去在进来还有的,就是用cookie存。

那为啥这个30天登录没有if标签呢?因为你点了自动登录就看不到这个页面了。

UserService service = BasicFactory.getFactory().getInstance(UserService.class);
		//1.获取用户名密码
		String username = request.getParameter("username");
		String password = MD5Utils.md5(request.getParameter("password"));
		//2.调用Service中根据用户名密码查找用户的方法
		User user = service.getUserByNameAndPsw(username,password);
		if(user == null){
			request.setAttribute("msg", "用户名密码不正确!");
			request.getRequestDispatcher("/login.jsp").forward(request, response);
			return;
		}
		//3.检查用户激活状态
		if(user.getState() == 0){
			request.setAttribute("msg", "用户尚未激活,请到邮箱中进行激活!");
			request.getRequestDispatcher("/login.jsp").forward(request, response);
			return;
		}
		//4.登录用户重定向到主页
		request.getSession().setAttribute("user", user);
		
		//--处理记住用户名
		if("true".equals(request.getParameter("remname"))){
			Cookie remnameC = new Cookie("remname",URLEncoder.encode(user.getUsername(),"utf-8"));
			remnameC.setPath("/");
			remnameC.setMaxAge(3600*24*30);
			response.addCookie(remnameC);
		}else{
			Cookie remnameC = new Cookie("remname","");
			remnameC.setPath("/");
			remnameC.setMaxAge(0);
			response.addCookie(remnameC);
		}
		
		//--处理30天内自动登陆
		if("true".equals(request.getParameter("autologin"))){
			Cookie autologinC = new Cookie("autologin",URLEncoder.encode(user.getUsername()+":"+user.getPassword(),"utf-8"));
			autologinC.setPath("/");
			autologinC.setMaxAge(3600*24*30);
			response.addCookie(autologinC);
		}
		
		response.sendRedirect("/index.jsp");

URLEncoder.encode(user.getUsername()+":"+user.getPassword(),"utf-8") ,这个是用来解码的。因为用户名包含中文。

记住用户名,那么我们必须去前端显示了,从cookie中取出来。

 <head>
	<script type="text/javascript">
		window.onload=function(){
	  		var str = decodeURI('${cookie.remname.value}');
	  		document.getElementsByName("username")[0].value = str;
  		}
  	</script>
  </head>

 本来这段js代码,需要写在最后,如果写在前面,getElements是获得不到的,因为页面还没加载过去。但是用了window.onload

可以在页面加载完毕,加载这个函数。  decodeURI('${cookie.remname.value}');将cookie中的值解码,注意cookie中是以map的

形式存值,所以要用cookie.remname.value的方式取值。document.getElementsByName("username")[0].value = str;然后直接

赋值就好了。我记得以前都是setAttribute赋值,但是这个节点的value可以直接赋值。

那30天登录怎么办呢?

做个自动登录的拦截器AutologinFilter

public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse resp = (HttpServletResponse) response;
		//1.只有未登录的用户才自动登录
		if(req.getSession(false)==null || req.getSession().getAttribute("user")==null){
			//2.只有带了自动登陆cookie的用户才自动登陆
			Cookie [] cs = req.getCookies();
			Cookie findC = null;
			if(cs!=null){
				for(Cookie c : cs){
					if("autologin".equals(c.getName())){
						findC = c;
						break;
					}
				}
			}
			if(findC!=null){
				//3.只有自动登录cookie中的用户名密码都正确才做自动登陆
				String v = URLDecoder.decode(findC.getValue(),"utf-8");
				String username = v.split(":")[0];
				String password = v.split(":")[1];
				UserService service = BasicFactory.getFactory().getInstance(UserService.class);
				User user = service.getUserByNameAndPsw(username, password);
				if(user!=null){
					req.getSession().setAttribute("user", user);
				}
			}
		}
		
		
		//4.无论是否自动登陆都要放行
		chain.doFilter(request, response);
	}

1.只有未登录的用户才自动登录2.只有带了自动登陆cookie的用户才自动登陆3.只有自动登录cookie中的用户名密码都正确才做自动登陆(拦截所有请求)

感觉这个写的有点牛逼,很严谨。尤其是最后一个,还要去数据库验证一下。(因为可能人为的改动数据)所以所有涉及到cookie的,存取时都应该跟数据库打交道。之后存入session后就好了,登录时会自动判断是否seesion有值。

牛逼!

所以退出注销时也不能只销毁session,还有cookie

if(request.getSession(false)!=null){
			request.getSession().invalidate();
			//删除自动登陆cookie
			Cookie autologinC = new Cookie("autologin","");
			autologinC.setPath("/");
			autologinC.setMaxAge(0);
			response.addCookie(autologinC);
		}
		response.sendRedirect("/index.jsp");

request.getSession(true):若存在会话则返回该会话,否则新建一个会话。

request.getSession(false):若存在会话则返回该会话,否则返回NULL

getSession(boolean create)意思是返回当前reqeust中的HttpSession ,如果当前reqeust中的HttpSession 为null,当create为true,就创建一个新的Session,否则返回null; 
简而言之: 
HttpServletRequest.getSession(ture)等同于 HttpServletRequest.getSession() 
HttpServletRequest.getSession(false)等同于 如果当前Session没有就为null; 

所以说如果只是想判断下session是否存在,那么HttpServletRequest.getSession(false),如果用

request.getSession()或者request.getSession(true)(两个都一样),不好意思,没有你还新建一个。

完!!!!!

猜你喜欢

转载自blog.csdn.net/BadRibbit/article/details/81747937