javaEE之--------統計網站線上人數,安全登入等(觀察者設計模式)
總體介紹下:
監聽器:監聽器-就是一個實現待定介面的普通Java程式,此程式專門用於監聽別一個類的方法呼叫。都是使用觀察者設計模式。
小弟剛接觸這個,做了些簡單的介紹,大神請繞道,技術只是一點點,方便以後遇到問題可以看這些能解決一些問題。同時希望和大家一起分享下自己寫的小小示例
在servlet中事件源:
事件源:HttpSession
HttpSessionListener -
HttpSessionAttributeListener
HttpSessionEvent
事件源:ServletRequest
ServletRequestListenr
ServletRequestAttributeListener
ServletRequestEvent
事件源:ServletContext
ServletContextListener
ServletContextAttributeListener
ServletContextEvent
在我們這個專案中用到知識點:
HttpSessionListener,監聽HttpSession的建立和銷燬。
sessionCreated
sessionDestroyed
Session的預設有效時間為30分。可以通過配置的方式修改它的值。
可以呼叫session.invalidate方法銷燬當前Session.
主要作用是記錄當前所有線上人數,無論是使用者還是遊客。·
上面是我們做這個例項用到的知識,下面具體介紹:
例項需求:
1、使用HttpSessionListener記錄線上人數。
2、使用HttpSessionAttributeListener記錄登入人數。
3、 將使用者踢出系統。
一:記錄線上人數
每一個使用者訪問網站都會有一個seesion的建立,所以我們從建立session的統計即可
寫一個類,實現HttpSessionListener介面
sessionCreated 函式和 sessionDestroyed ,我們只用到建立sessionCreated
把建立的session全部放在一個map集合中,當前臺需要獲取的時候,直接從context中獲取,進行其他操作。其實 sessionCreated 用到集合上鎖,api自帶,解決多執行緒問題。
public class MySessionListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent se) { //我們把建立的session封裝在一個map中 Map<String, HttpSession> map =(Map<String, HttpSession>) se.getSession().getServletContext().getAttribute("onLines"); if(map==null){//說明這是第一次訪問是,需要自己new 一個物件 map=Collections.synchronizedMap(new HashMap<String, HttpSession>());//採用集合上鎖,採用java 自帶的上鎖函式 se.getSession().getServletContext().setAttribute("onLines", map); } // System.out.println("listener新增一個了"); map.put(se.getSession().getId(), se.getSession());//以session 的id為key,session物件為value存在map中 } @Override public void sessionDestroyed(HttpSessionEvent se) { }}
二,前臺顯示頁面
我們採用登入前後,都在一個頁面顯示,採用jstl表示式進行區別就可以。其中,我們用到當登入成功之後,我們就直接採用設定session,讓其區別就可以,當我們採用安全登入也是採用這個session裡面是都存在值。
<body>
<!-- 需求:做一個能夠允許遊客訪問,允許登入,可以檢視當前使用者(包括遊客),
資訊-----name,ip,createTime,lastTime,需要一個許可權是都可以踢人(使session失效)
就是講session建立的時候,存一個list中去,然後相關的資訊從session中回去就可以 -->
<h2>這是使用者登入介面</h2>
<c:if test="${empty sessionScope.user}" var="boo">
<form action="<c:url value='/LoginServlet'/>" method="post">
NAME:<input type="text" name="name"/><br/>
PWD :<input type="text" name="pwd"/><br/>
<input type="submit" value="提交資訊"/>
</form>
</c:if>
<c:if test="${!boo }">
歡迎您。${sessionScope.user.name}
<a href="<c:url value='/servlet/ShowLoginServet'/>" >顯示當前訪問數量</a>
<a href="<c:url value='/servlet/LoginoutServlet'/>">退出</a>
</c:if>
<hr/>
<a href="<c:url value='/jsps/show.jsp'/>">這是真實使用者能訪問的使用者</a>
<a href="<c:url value='/open/open.jsp'/>">遊客能訪問的內容</a>
</body>
三,登入之後處理
在是否登入,我們簡單的模擬下
成功之後我們就設定一個session的值傳給前臺處理
<span style="font-size:18px;"> public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//接收引數的編碼問題已經通過 過濾器設定好
String name=request.getParameter("name");
String pwd =request.getParameter("pwd");
//獲得引數之後,開始封裝資料
User user =new User();
user.setName(name);
user.setPwd(pwd);
user.setAdmin(true);
//封裝資料之後,呼叫service層,訪問資料庫,簡單的模擬 當name和pwd相等就判斷登入成功
if(name!=null && !name.trim().equals("") && pwd != null && pwd.trim().length()>0 ){
if(name.endsWith(pwd)){//模擬下,相等就是登入成功
request.getSession().setAttribute("user", user);
}
}
response.sendRedirect(request.getContextPath()+"/index.jsp");//重定向到主頁
}</span>
四,檢視線上使用者<span style="font-size:14px;"> <body>
<h2>這是使用者登入擁有的資源</h2>
<!-- 前臺最後採用list<map《》>的封裝資訊 -->
<!-- 顯示介面,需要知道session裡面的資訊,採用監聽器 要監聽遊客,和 使用者,採用httpSession-->
歡迎您。。${user.name }<br/>
<table >
<tr>
<td>姓名</td>
<td>IP</td>
<td>建立時間</td>
<td>最近訪問時間</td>
<td>操作</td>
</tr>
<c:forEach items="${requestScope.list }" var="map">
<tr>
<td>
<c:if test="${ empty map.user }" var="boo">
遊客
</c:if>
<c:if test="${!boo }">
${map.user.name }
</c:if>
</td>
<td>
${map.ip }
</td>
<td>
<fmt:formatDate value="${map.createTime }" pattern="yyyy-MM-dd HH-mm-ss"/>
</td>
<td>
<fmt:formatDate value="${map.LastAccessedTime }" pattern="yyyy-MM-dd HH-mm-ss"/>
</td>
<td>
<c:if test="${!boo && map.user.admin }">
<!-- <a href="<c:url value='/servlet/KitLogin'/>?id=${map.id }&&user=${user.name}">踢人</a> -->
<a href="/onlineWeb/servlet/KitLogin?id=${map.id }&&user=${user.name}">踢人</a>
</c:if>
</td>
</tr>
</c:forEach>
</table> </span>
檢視當前使用者線上人數,我們直接收集引數:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//在這裡,我們需要將前臺需要的資訊在這裡封裝
//從onLines《sessionID,session物件中》拿到整個session集合,提取其中的資訊
//然後把這個資訊封裝起來轉給前臺顯示
List<Map<String, Object>> list=new ArrayList<Map<String,Object>>();//採用list裝資料
Map<String, HttpSession> onLines=(Map<String, HttpSession>) request.getSession().getServletContext().getAttribute("onLines");
// System.out.println(onLines);
Iterator<Map.Entry<String, HttpSession>> it= onLines.entrySet().iterator();
while(it.hasNext()){
Entry<String, HttpSession> entry=it.next();
HttpSession sess=entry.getValue();//拿到單個的session物件了
Map<String, Object> mm =new HashMap<String, Object>();//採用map封裝一行資料,然後放在list 中去,就是一個表的資料
mm.put("id", sess.getId());//獲取session的id
mm.put("createTime",new Date(sess.getCreationTime()));//建立的時間.傳過去的是date型別我們前臺進行解析,顯示出來
mm.put("LastAccessedTime", new Date(sess.getLastAccessedTime()));//上次訪問的時間
mm.put("user", sess.getAttribute("user"));
mm.put("ip", sess.getAttribute("ip"));
//前臺需要的資訊補全,後臺去使用
list.add(mm);
}
request.setAttribute("list", list);
request.getRequestDispatcher("/jsps/show.jsp").forward(request, response);//呆著list物件跳轉的顯示頁面
}
效果圖:
五,管理員踢人
這裡全部設定為管理員,可以在值物件裡面修改
主要是在點選踢人時,將該物件的id傳過來,踢人就是把使用者的session.invalidate,設定相同的使用者名稱,不能踢。
踢人時,我們不僅僅只是改session。還需要將map物件裡面的session移除。
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter pw =response.getWriter();
String id=request.getParameter("id");
String username=request.getParameter("user");//當前頁的user
Map<String, HttpSession> map=(Map<String, HttpSession>) request.getServletContext().getAttribute("onLines");
HttpSession se=map.get(id);//通過id 得到session物件
User user= (User) se.getAttribute("user"); //通過session物件可以得到user物件
System.out.println("username傳過來的www"+username);
System.out.println("本地直接獲得的www"+user.getName());
if(!user.getName().equals(username)){//不能刪除和自己同名的使用者
if(map.containsKey(id)){
System.out.println("已經刪除");
HttpSession ss= map.get(id);
map.remove(id);//從context裡面移除
ss.invalidate();//讓session失效
pw.write("成功刪除使用者");
}else{
pw.write("該使用者已被刪除");
}
request.getRequestDispatcher("/jsps/show.jsp");
}else{
pw.write("使用者不能踢自己");
}
pw.write("<a href='/onlineWeb/index.jsp'>返回</a>");
}
六,主動退出
直接將自己的session移除,和把自己的從map中移除
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Map<String, HttpSession> map =(Map<String, HttpSession>) request.getServletContext().getAttribute("onLines");
String id=request.getSession().getId();
map.remove(id);//通過id來刪除,context大容器中的session物件
request.getSession().invalidate();
response.sendRedirect(request.getContextPath()+"/index.jsp");
}
七,安全登入
以前我們寫得示例,只要正確輸入了路徑和訪問的專案,我們就能直接訪問,沒有安全性可言,但是現在我們可以利用
過濾器來實現這個功能。
我們的依據是我們登入成功之後,我們將session裡面設定值,然後我們就可以根據這個來過濾了,當前我們知道,之前的全站壓縮和過濾敏感詞等,都是需要配置過濾器路徑,所以我們在配置路徑是需要注意,登入介面和處理登入介面的結果是不能被過濾的。所以一般這兩個都是直接放在根目錄下的。
public class SafeLoginrFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req=(HttpServletRequest) request;
HttpServletResponse resp=(HttpServletResponse) response;
if(req.getSession().getAttribute("user")==null){//這是過濾非法使用者使用的,只有登入的使用者才能進入使用者許可權
resp.sendRedirect(req.getContextPath()+"/index.jsp");
}else{
chain.doFilter(req, resp);
}
}
@Override
public void destroy() {
}
根據上面的這個,我們可以設定一些目錄可以讓遊客訪問,就是不用通過過濾器的頁面,單獨寫一個資料夾都可以。
攔截路徑在web.xml中配置:
<filter-mapping>
<filter-name>safeLogin</filter-name>
<url-pattern>/servlet/*</url-pattern>
<url-pattern>/jsps/*</url-pattern>
</filter-mapping>
總結:在示例中,我們只需要知道,用HttpSessionListener,我們用它可以實現建立了多少session物件了,將他放到一個map容器中,需要的時候取出來就行了,當然,session中也存在很多有價值的,如id,訪問時間,最後一次訪問時間等。其餘的都是以前常用的知識。