经过对业务逻辑的完善,解决了一部分问题,但还是有一部分问题没有解决

经过对业务逻辑的完善,解决了一部分问题,但还是有一部分问题没有解决

在解决问题的过程中,也意识到了一些细节的重要性。在网页每次向服务端发出请求时,doget方法中的实例对象是(比如集合对象,用户信息对象)都会被重新创建吗?因为每次发出新的请求的时候,doget方法中都是在执行集合对象为空的代码。后面我直接将集合对象loginList创建在用户信息类LoginMessage类中,对象也在servlet全局中声明,解决了这一问题(如下)http://img1.sycdn.imooc.com//climg/6034fc3f09939fdf07410367.jpg



未解决的问题(如下):

  1. 已经判断出了用户新登入的账户已登录,但是remove方法还是没有执行,是调用的remove方法不对还是?

    http://img1.sycdn.imooc.com//climg/6034fd440938d5e208670356.jpg

2. 在踢掉之前登录的账户,我的思路大概是这样的,LoginMessage对象保存用户的账户以及密码,把LoginMessage对象存入List集合中,然后通过遍历List集合,判断新传入的用户账户是否已存在,如果已存在,则把list集合已存在的用户账户调用remove方法,再到监听器remove方法中循环遍历,找出与之对应的sessionID,在将其删除。  在返回到LoginServlet类中,将新传入的用户信息加入。整体是这样的一样思路。







相关代码:servlet业务逻辑实现类

package com.roan.login;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * Servlet implementation class LoginServlet
 */
@WebServlet("/servlet/login")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// 声明用户信息对象loginMessage
private LoginMessage loginM=null;
// 创建用户会话对象,存储用户信息
private HttpSession session =null;
/**
 * @see HttpServlet#HttpServlet()
 */
public LoginServlet() {
super();
loginM=new LoginMessage();
}

/**
 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
 *      response)
 */
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
boolean flag=false;
session=request.getSession();
// 获取login界面传来的用户名密码参数
String name = request.getParameter("userName");
String pwd = request.getParameter("pwd");

if(name!=null&&pwd!=null) {
// 用户信息对象为空时,用户信息直接添加,用户会话属性直接赋值
if(!loginM.getLoginList().isEmpty()) {
// 用户信息对象不为空时,则先判断用户信息是否已存在?如不存在则添加,如存在则删除上一个用户会话对象
for(LoginMessage l:loginM.getLoginList()) {
if(l.getUserName().equals(name)) {
System.out.println("该用户账户已经登录");
// 调用remove方法,将之前的用户信息传值
session.removeAttribute(l.getUserName());
// 将新登入的用户信息传入,赋予新的用户会话ID
session.setAttribute("name", name);
flag=true;
break;
}else {
System.out.println("新用户持续登录中");
session.setAttribute("name", name);
loginM.setPwd(pwd);
loginM.setUserName(name);
loginM.getLoginList().add(loginM);
flag=true;
}
}
}else {
session.setAttribute("name", name);
loginM.setPwd(pwd);
loginM.setUserName(name);
loginM.getLoginList().add(loginM);
System.out.println("用户第一次登录成功");
flag=true;
}
}

// 发送请求,将该请求下发到登录成功页面
if(flag){
request.setAttribute("name", name);
response.sendRedirect("/user-login-project/jsp/login-success.jsp");
}
}
}

相关代码:监听器实现类

package com.roan.login;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;


public class SessionAttributeListener implements HttpSessionAttributeListener{
// 声明用户会话ID和用户账户Map集合
private Map<String,String> loginMap=null;


public SessionAttributeListener() {
super();
 loginMap=new HashMap<>();
}

@Override
public void attributeAdded(HttpSessionBindingEvent event) {
System.out.println("新增属性:");
System.out.println(event.getSession().getId()+":"+event.getValue().toString());
loginMap.put(event.getSession().getId(), event.getValue().toString());
}

@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
System.out.println("删除属性"+event.getValue());

// 遍历集合loginMap,找出用户账户与之对应的用户会话ID并删除
Set<Entry<String,String>> entrySet=loginMap.entrySet();
for(Entry<String,String> entry:entrySet) {
if(entry.getValue().equals(event.getValue())){
event.getSession().removeAttribute(entry.getKey());
}
}
}

@Override
public void attributeReplaced(HttpSessionBindingEvent event) {
System.out.println("修改属性属性");
System.out.println(event.getSession().getId()+":"+event.getValue());
loginMap.put(event.getSession().getId(), event.getValue().toString());
}
}

相关代码:用户信息实体类

package com.roan.login;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class LoginMessage {
private String userName;//用户账户
private String pwd;//用户密码
private List<LoginMessage> loginList;



public LoginMessage() {
loginList=new ArrayList<>();
}

public LoginMessage(String userName, String pwd) {
super();
this.userName = userName;
this.pwd = pwd;
}


public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getPwd() {
return pwd;
}

public void setPwd(String pwd) {
this.pwd = pwd;
}

public List<LoginMessage> getLoginList() {
return loginList;
}

public void setLoginList(List<LoginMessage> loginList) {
this.loginList = loginList;
}

}

相关代码:过滤器实现类

package com.roan.login;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class isFilter implements Filter{
@Override
public void destroy() {
System.out.println("过滤器销毁");
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain arg2)
throws IOException, ServletException {
HttpServletRequest re=(HttpServletRequest)request;
// 拦截当前浏览网页的URL
String url=re.getRequestURI().toString();
// 判断用户是否登录
if(url.startsWith("/user-login-project/jsp/login-success")){
System.out.println("用户已登录 url="+url);

}else if(url.startsWith("/user-login-project/user-login")){
System.out.println("用户未登录 url="+url);

}
arg2.doFilter(request, response);
}

@Override
public void init(FilterConfig arg0) throws ServletException {
System.out.println("初始化");

}

}

相关代码:登录界面

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>用户登录</title> 
<script type="text/javascript" src="js/jquery-3.3.1.min.js"></script>
</head>
<body>
<div id="login_h">
<div id="login_h_title">
<h1>系统登录</h1>
</div>
<div id="login_body">
<form id="body_form" action="http://localhost:8080/user-login-project/servlet/login" method="get" >
<p>用户名:
</p>
<input type="text"  id="userName" name="userName" placeholder="请输入中文/字母/数字的用户名【4-7位】" 
/>
<p>密码:

</p>
<input type="password" id="pwd" name="pwd" placeholder="字母开头,数字字母6-10位组成"/ ><br>
<input type="submit" id="sub" name="sub" value="登录">
</form>
</div>
</div>

<script type="text/javascript">
$.ajax({
"url":"http://localhost:8080/user-login-project/servlet/login",
"type":"get",
"dataType":"json",
"success":function(json){

}
})

/* 给表单添加提交事件 */
/* 给相应的输入框添加正则表达式,限制输入内容 */
document.getElementById("body_form").onsubmit=function(){
var regexUserName=/^[\u4e00-\u9fa5A-Za-z1-9]{1}[\u4e00-\u9fa5A-Za-z0-9]{3,6}$/;
var regexPwd=/^[A-Za-z]{1}[0-9A-Za-z]{5,9}$/;

var userName=document.getElementById("userName").value;
var pwd=document.getElementById("pwd").value;

if(regexUserName.test(userName)==false){
alert("用户名不符合当前格式!");
return false;
}else if(regexPwd.test(pwd)==false){
alert("密码不不符合格式");
return false;
}else{
alert("校验成功!");
}
}
</script> 
</body>
</html>

相关代码:登录成功页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<script type="text/javascript" src="js/jquery-3.3.1.min.js"></script>
<head>
<script type="text/javascript">


</script>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1 style="color:red;">您好,${sessionScope.name},您已登录!</h1>
</body>
</html>


正在回答 回答被采纳积分+1

登陆购买课程后可参与讨论,去登陆

2回答
好帮手慕阿满 2021-02-24 15:37:51

同学你好,需要删除上一个用户账户对应的session ID值,新登录的用户账户需要重新调用监听器中的新增方法。同学可以参考如下代码:

LoginMessage类:

该类是单例,有存储用户登录sessionID和用户登录session的Map集合,有通过用户名查询和设置sessionID的方法,以及通过sessionID查找和设置session的方法。

public class LoginMessage {
private static LoginMessage instance = new LoginMessage();

private Map<String,String> loginUserSession = new HashMap<String,String>();// key值:登录用户登录名,value值:登录用户sessionId
private Map<String, HttpSession> loginSession = new HashMap<String,HttpSession>();//key值:登录用户sessionId,value值:登录用户session对象

private LoginMessage(){

}
public static LoginMessage getInstance(){
return instance;
}

public String getSessionIdByUsername(String username){
return loginUserSession.get(username);
}
public HttpSession getSessionBySessionId(String sessionId){
return loginSession.get(sessionId);
}
public void setSessionIdByUserName(String username,String sessionId){
loginUserSession.put(username, sessionId);
}
public void setSessionBySessionId(String sessionId,HttpSession session){
loginSession.put(sessionId, session);
}
}

LoginServlet类:

该类中只需要获取登录用户信息,将其存入session,转发到主界面的代码,如:

@WebServlet("/servlet/login")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;


public LoginServlet() {
super();
}

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {


// 发送请求,将该请求下发到登录成功页面
String username = request.getParameter("userName");
String password = request.getParameter("password");
HttpSession session = request.getSession();
session.setAttribute("loginUser", username);//登录完成,将登录用户名存储至session对象
response.sendRedirect("/success.jsp");
}
}

isFilter类(类命名不规范,首字母应该大写)

该类中只用来判断是否登录,如果登录,则跳转到主界面,没有登录则跳转到登录页面:

public class isFilter implements Filter{
@Override
public void destroy() {
System.out.println("过滤器销毁");
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain arg2)
throws IOException, ServletException {
HttpServletRequest hrequest = (HttpServletRequest)request;
HttpServletResponse hresponse = (HttpServletResponse)response;
String loginUser = (String)hrequest.getSession().getAttribute("loginUser");
// 判断用户是否登录
if(loginUser==null){
hresponse.sendRedirect(hrequest.getContextPath()+"/login.jsp?flag=1");
return;
}else{
arg2.doFilter(request, response);
return;
}
}

LoginSessionListener监听器类:

该类的attributeAdded方法中获取监听到的属性名,如果是登录操作,则获取登录的名称,判断登录名称是否之前登录过,如果登录过,则清理前次会话信息,将本次登录信息存入LoginMessage。如:

public class SessionAttributeListener implements HttpSessionAttributeListener{
private static final String LOGIN_USER="loginUser";

public SessionAttributeListener() {
super();
}

@Override
public void attributeAdded(HttpSessionBindingEvent event) {
String attrName = event.getName();
if(LOGIN_USER.equals(attrName)){//若属性名为登录属性名,判定为用户登录操作
String attrVal = (String)event.getValue();//获取添加的属性值,即用户登录名
HttpSession session = event.getSession();//该次操作的session对象
String sessionId = session.getId();//该次操作的session对象ID

String sessionId2 = LoginMessage.getInstance().getSessionIdByUsername(attrVal);//从缓存对象里面,获得该用户登录名对应的sessionID值
if(null == sessionId2){//未获得结果,不需要清理前次登录用户会话信息

}else{
HttpSession session2 = LoginMessage.getInstance().getSessionBySessionId(sessionId2);//获取前次该用户登录对应的session对象
session2.invalidate();//清理前次登录用户会话存储信息,使得前次登录失效
}

//完成该次登录用户登录名、sessionID,session对象的缓存对象存储
LoginMessage.getInstance().setSessionIdByUserName(attrVal, sessionId);
LoginMessage.getInstance().setSessionBySessionId(sessionId, session);
}
}

@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
}

@Override
public void attributeReplaced(HttpSessionBindingEvent event) {
}

}

​同学可以参考如上代码进行理解。

祝学习愉快~

  • 原子Q #1

    老师,请我你这个的filter需要在web.xml中进行配置吗?如果需要的话,url-pattern该拦截哪些呢?

    2021-05-19 18:22:51
  • 同学你好,过滤器需要在web.xml中进行配置。url-pattern中可以对所有的路径进行拦截,即/*,在过滤器中对登录的路径进行放行。​

    祝学习愉快~

    2021-05-19 19:20:19
  • 那我疑惑的一点就是在过滤器中直接判断获取到登入页面的用户名和密码,如果不为空则在过滤器中放行,这样它是不是也可以放行呢?还是必须需要先判断类似于是否是HTML,jsp,或者其他的文件类型先呢?

    2021-05-19 19:57:26
好帮手慕阿满 2021-02-24 13:49:57

同学你好,关于同学的问题:

1、remove方法已经执行,同学调用remove方法删除用户信息后,又使用setAttribute()添加用户,如:

http://img1.sycdn.imooc.com//climg/6035e93309c09aa914810529.jpg


所以并不能实现互踢功能。


2、同学的思路大致是正确的,但是代码和思路有所不同。如下所示是执行过程中的一些输出信息,sessionID是不同,所以我们删除的应该是sessionID,对sessionID进行判断,而不是name,如:

http://img1.sycdn.imooc.com//climg/6035e94509e5590a11000398.jpg

祝学习愉快~

  • 提问者 大吉他 #1

    老师您好,监听器中的删除方法是通过用户账户名遍历查找与之对应的session ID,在删除key值,也就是sessionID。
    说到这里我有一个疑问了:相同的用户账户在另外一个浏览器登入,需要删除上一个用户账户对应的session ID值,那新登录的用户账户不要重新调用监听器中的新增方法,执行增加吗?

    2021-02-24 14:25:36
问题已解决,确定采纳
还有疑问,暂不采纳

恭喜解决一个难题,获得1积分~

来为老师/同学的回答评分吧

0 星
请稍等 ...
意见反馈 帮助中心 APP下载
官方微信

在线咨询

领取优惠

免费试听

领取大纲

扫描二维码,添加
你的专属老师