Browse Source
- pending issue: header info box not updated right after login.feature/jQueryMainPage
13 changed files with 467 additions and 25 deletions
@ -0,0 +1,3 @@ |
|||||||
|
<Context> |
||||||
|
<CookieProcessor sameSiteCookies="strict" /> |
||||||
|
</Context> |
||||||
@ -0,0 +1 @@ |
|||||||
|
samesite-cookie(mode=Lax) |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
<!DOCTYPE html><%@ |
||||||
|
page import="java.util.Collection,org.leolo.web.dm.dao.*,org.leolo.web.dm.model.*,org.leolo.web.dm.util.*" |
||||||
|
%><html> |
||||||
|
<head> |
||||||
|
<title>Download Manager</title> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<h1>Download Manager</h1> |
||||||
|
<% Collection<Project> projs = new ProjectDao().getAllProjects(); %> |
||||||
|
<h2>Project List</h2> |
||||||
|
<ul><% for(Project proj:projs){ %> |
||||||
|
<li> |
||||||
|
<a href="project/<%=proj.getProjectPath()%>"><%= proj.getProjectName() %></a><br> |
||||||
|
<%= JSPUtils.nl2br(proj.getDescription()) %> |
||||||
|
</li> |
||||||
|
<%} %></ul> |
||||||
|
</body> |
||||||
|
</html> |
||||||
@ -1,18 +1,100 @@ |
|||||||
<!DOCTYPE html><%@ |
<!DOCTYPE html> |
||||||
page import="java.util.Collection,org.leolo.web.dm.dao.*,org.leolo.web.dm.model.*,org.leolo.web.dm.util.*" |
<html> |
||||||
%><html> |
|
||||||
<head> |
<head> |
||||||
|
<meta charset="ISO-8859-1"> |
||||||
<title>Download Manager</title> |
<title>Download Manager</title> |
||||||
|
<script src="js/jquery-3.6.0.min.js"></script> |
||||||
|
<script src="js/jquery-ui.js"></script> |
||||||
|
<link href="js/jquery-ui.css" rel="stylesheet"> |
||||||
|
<link href="js/main.css" rel="stylesheet"> |
||||||
|
<script> |
||||||
|
function updateUserInfo(){ |
||||||
|
$.get( '<%=request.getContextPath()%>/UserInfo' , function(data){ |
||||||
|
$('#sysname').html(data.system-name); |
||||||
|
if(data.identified){ |
||||||
|
//Handle userinfo box and logout link |
||||||
|
$("#hdr-action").text("Welcome back, "+data.username); |
||||||
|
$("#hdr-action").click(function(){ |
||||||
|
$("#hdr-uinfo").toggle(); |
||||||
|
}); |
||||||
|
}else{ |
||||||
|
//Display login link |
||||||
|
$("#hdr-action").text("Login"); |
||||||
|
$("#hdr-action").click(function(){ |
||||||
|
$("#hdr-login").toggle(); |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
</script> |
||||||
</head> |
</head> |
||||||
<body> |
<body> |
||||||
<h1>Download Manager</h1> |
<nav> |
||||||
<% Collection<Project> projs = new ProjectDao().getAllProjects(); %> |
Download Manager - <span id="sysname"></span> |
||||||
<h2>Project List</h2> |
<span class="status" id="hdr-action">Login</span> |
||||||
<ul><% for(Project proj:projs){ %> |
<div class="hidden hdr-info-box ui-corner-all" id="hdr-uinfo"> |
||||||
<li> |
<button id="hdr-uinfo-logout">Logout</button> |
||||||
<a href="project/<%=proj.getProjectPath()%>"><%= proj.getProjectName() %></a><br> |
</div> |
||||||
<%= JSPUtils.nl2br(proj.getDescription()) %> |
<div class="hidden hdr-info-box ui-corner-all" id="hdr-login"> |
||||||
</li> |
<div class="hidden ui-state-error ui-corner-all" style="padding:0.3em;" id="hdr-login-msg"> |
||||||
<%} %></ul> |
<span class="ui-icon ui-icon-alert"></span> |
||||||
|
<span id="hdr-login-msg-cont"></span> |
||||||
|
</div> |
||||||
|
<table> |
||||||
|
<tr> |
||||||
|
<th><label for="hdr-login-username" style="white-space: nowrap;">Username</label></th> |
||||||
|
<td><input type="text" id="hdr-login-username"></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<th><label for="hdr-login-password">Password</label></th> |
||||||
|
<td><input type="password" id="hdr-login-password"></td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td colspan="2"><button id="hdr-login-submit">Submit</button></td> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
</div> |
||||||
|
</nav> |
||||||
|
<main> |
||||||
|
|
||||||
|
</main> |
||||||
|
<script> |
||||||
|
//Perform post-load task |
||||||
|
updateUserInfo(); |
||||||
|
$("#hdr-login-submit").button(); |
||||||
|
//$("#hdr-action").button(); |
||||||
|
$("#hdr-login-submit").click(function(){ |
||||||
|
var uname = $("#hdr-login-username").val(); |
||||||
|
var pass = $("#hdr-login-password").val(); |
||||||
|
console.log("uname="+uname); |
||||||
|
console.log("paswd="+pass); |
||||||
|
if(uname==null || pass==null || uname=="" ||pass==""){ |
||||||
|
//Display error message |
||||||
|
$("#hdr-login-msg-cont").text("Username and password are required."); |
||||||
|
$("#hdr-login-msg").show(); |
||||||
|
return; |
||||||
|
} |
||||||
|
$.post('<%=request.getContextPath()%>/Login', |
||||||
|
{username: uname, password: pass}, |
||||||
|
function(data){ |
||||||
|
if(data.status=="failed"){ |
||||||
|
$("#hdr-login-msg-cont").text(data.message); |
||||||
|
$("#hdr-login-msg").show(); |
||||||
|
}else{ |
||||||
|
updateUserInfo(); |
||||||
|
$("#hdr-login-msg").hide(); |
||||||
|
$("#hdr-login").hide(); |
||||||
|
} |
||||||
|
}) |
||||||
|
}); |
||||||
|
$("#hdr-uinfo-logout").button(); |
||||||
|
$("#hdr-uinfo-logout").click(function (){ |
||||||
|
$.post('<%=request.getContextPath()%>/Logout', function(data){ |
||||||
|
updateUserInfo(); |
||||||
|
$("#hdr-uinfo").hide(); |
||||||
|
|
||||||
|
}); |
||||||
|
}); |
||||||
|
</script> |
||||||
</body> |
</body> |
||||||
</html> |
</html> |
||||||
@ -0,0 +1,82 @@ |
|||||||
|
package org.leolo.web.dm.dao; |
||||||
|
|
||||||
|
import java.sql.Connection; |
||||||
|
import java.sql.PreparedStatement; |
||||||
|
import java.sql.ResultSet; |
||||||
|
import java.sql.SQLException; |
||||||
|
import java.util.Hashtable; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import org.leolo.web.dm.Constant; |
||||||
|
import org.leolo.web.dm.model.User; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
public class UserDao extends BaseDao { |
||||||
|
|
||||||
|
private static Logger log = LoggerFactory.getLogger(UserDao.class); |
||||||
|
|
||||||
|
public Map<String, Object> getBasicUserInfoByUsernameWithPassword(String username) { |
||||||
|
Map<String, Object> u = new Hashtable<>(); |
||||||
|
try (Connection conn = getConnection()) { |
||||||
|
// Step 1: get basic user info
|
||||||
|
try (PreparedStatement pstmt = conn |
||||||
|
.prepareStatement("SELECT user_id, user_name, password, pwd_err_cnt FROM user WHERE user_name = ?")) { |
||||||
|
pstmt.setString(1, username); |
||||||
|
try (ResultSet rs = pstmt.executeQuery()) { |
||||||
|
if (rs.next()) { |
||||||
|
u.put(Constant.BUI_KEY_USER_NAME, rs.getString(2)); |
||||||
|
u.put(Constant.BUI_KEY_USER_ID, rs.getInt(1)); |
||||||
|
u.put(Constant.BUI_KEY_PASSWORD, rs.getString(3)); |
||||||
|
u.put(Constant.BUI_KEY_FAIL_CNT, rs.getInt(4)); |
||||||
|
} else { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} catch (SQLException e) { |
||||||
|
log.error(e.getMessage(), e); |
||||||
|
return null; |
||||||
|
} |
||||||
|
return u; |
||||||
|
} |
||||||
|
|
||||||
|
public void updateLastLogin(int userId) { |
||||||
|
try( |
||||||
|
Connection conn = getConnection(); |
||||||
|
PreparedStatement pstmt = conn.prepareStatement("UPDATE user SET pwd_err_cnt = 0, last_login=NOW() WHERE user_id = ?") |
||||||
|
){ |
||||||
|
pstmt.setInt(1, userId); |
||||||
|
pstmt.executeUpdate(); |
||||||
|
}catch(SQLException e) { |
||||||
|
log.error(e.getMessage(), e); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void updateLoginFailedCount(int userId) { |
||||||
|
try( |
||||||
|
Connection conn = getConnection(); |
||||||
|
PreparedStatement pstmt = conn.prepareStatement("UPDATE user SET pwd_err_cnt = nvl(pwd_err_cnt,0) + 1 WHERE user_id = ?") |
||||||
|
){ |
||||||
|
pstmt.setInt(1, userId); |
||||||
|
pstmt.executeUpdate(); |
||||||
|
}catch(SQLException e) { |
||||||
|
log.error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void lockUser(int userId) { |
||||||
|
try( |
||||||
|
Connection conn = getConnection(); |
||||||
|
PreparedStatement pstmt = conn.prepareStatement("UPDATE user SET password = concat('!!',password) WHERE user_id = ? AND password NOT LIKE '!!%'") |
||||||
|
){ |
||||||
|
pstmt.setInt(1, userId); |
||||||
|
pstmt.executeUpdate(); |
||||||
|
|
||||||
|
}catch(SQLException e) { |
||||||
|
log.error(e.getMessage(), e); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
@ -1,5 +1,42 @@ |
|||||||
package org.leolo.web.dm.model; |
package org.leolo.web.dm.model; |
||||||
|
|
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
public class User { |
public class User { |
||||||
|
|
||||||
|
private String userName; |
||||||
|
private int userId; |
||||||
|
private String password; |
||||||
|
|
||||||
|
private Set<String> roles = new HashSet<>(); |
||||||
|
|
||||||
|
public String getUserName() { |
||||||
|
return userName; |
||||||
|
} |
||||||
|
|
||||||
|
public void setUserName(String userName) { |
||||||
|
this.userName = userName; |
||||||
|
} |
||||||
|
|
||||||
|
public int getUserId() { |
||||||
|
return userId; |
||||||
|
} |
||||||
|
|
||||||
|
public void setUserId(int userId) { |
||||||
|
this.userId = userId; |
||||||
|
} |
||||||
|
|
||||||
|
public String getPassword() { |
||||||
|
return password; |
||||||
|
} |
||||||
|
|
||||||
|
public void setPassword(String password) { |
||||||
|
this.password = password; |
||||||
|
} |
||||||
|
|
||||||
|
public Set<String> getRoles() { |
||||||
|
return roles; |
||||||
|
} |
||||||
|
|
||||||
} |
} |
||||||
|
|||||||
@ -0,0 +1,96 @@ |
|||||||
|
package org.leolo.web.dm.servlet; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
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 org.json.JSONObject; |
||||||
|
import org.leolo.web.dm.Constant; |
||||||
|
import org.leolo.web.dm.dao.SystemParameterDao; |
||||||
|
import org.leolo.web.dm.dao.UserDao; |
||||||
|
import org.leolo.web.dm.util.QueuedJobs; |
||||||
|
import org.leolo.web.dm.util.ServletUtil; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
import org.slf4j.Marker; |
||||||
|
import org.slf4j.MarkerFactory; |
||||||
|
|
||||||
|
import at.favre.lib.crypto.bcrypt.BCrypt; |
||||||
|
|
||||||
|
/** |
||||||
|
* Servlet implementation class LoginServlet |
||||||
|
*/ |
||||||
|
@WebServlet("/Login") |
||||||
|
public class LoginServlet extends HttpServlet { |
||||||
|
private static Logger log = LoggerFactory.getLogger(LoginServlet.class); |
||||||
|
private static Marker mark = MarkerFactory.getMarker("user"); |
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
/** |
||||||
|
* @see HttpServlet#HttpServlet() |
||||||
|
*/ |
||||||
|
public LoginServlet() { |
||||||
|
super(); |
||||||
|
// TODO Auto-generated constructor stub
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) |
||||||
|
*/ |
||||||
|
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { |
||||||
|
// TODO Auto-generated method stub
|
||||||
|
UserDao udao = new UserDao(); |
||||||
|
SystemParameterDao spdao = new SystemParameterDao(); |
||||||
|
log.info("Login attempted! {}@{}:{}", request.getParameter("username"), request.getRemoteHost(), request.getParameter("password")); |
||||||
|
Map<String, Object> bui = udao.getBasicUserInfoByUsernameWithPassword(request.getParameter("username")); |
||||||
|
if(bui!=null) { |
||||||
|
if(BCrypt.verifyer().verify(request.getParameter("password").toCharArray(), bui.get(Constant.BUI_KEY_PASSWORD).toString().toCharArray()).verified) { |
||||||
|
log.info(mark, "User {}@{} logged in successfully!", bui.get(Constant.BUI_KEY_USER_NAME), ServletUtil.getClientIpAddr(request)); |
||||||
|
request.getSession().setAttribute(Constant.SESSION_USER_ID, (int)bui.get(Constant.BUI_KEY_USER_ID)); |
||||||
|
request.getSession().setAttribute(Constant.SESSION_USER_NAME, bui.get(Constant.BUI_KEY_USER_NAME)); |
||||||
|
sendSuccessResponse(response); |
||||||
|
QueuedJobs.getInstance().queue(()->{ |
||||||
|
udao.updateLastLogin((int)bui.get(Constant.BUI_KEY_USER_ID)); |
||||||
|
}); |
||||||
|
}else { |
||||||
|
log.info(mark, "User {}@{} attempted to login![ICP/UL]", bui.get(Constant.BUI_KEY_USER_NAME), ServletUtil.getClientIpAddr(request)); |
||||||
|
sendFailedResponse(response); |
||||||
|
QueuedJobs.getInstance().queue(()->{ |
||||||
|
udao.updateLoginFailedCount((int)bui.get(Constant.BUI_KEY_USER_ID)); |
||||||
|
int failedCount = (int) bui.get(Constant.BUI_KEY_FAIL_CNT); |
||||||
|
failedCount++; |
||||||
|
if(failedCount >= spdao.getInt(Constant.SP_MAX_LOGIN_FAIL_CNT, 5)) { |
||||||
|
//lock account
|
||||||
|
udao.lockUser((int)bui.get(Constant.BUI_KEY_USER_ID)); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
}else { |
||||||
|
//User does not exist!
|
||||||
|
log.info(mark, "User {}@{} attempted to login![UNE]", request.getParameter("username"), ServletUtil.getClientIpAddr(request)); |
||||||
|
BCrypt.withDefaults().hashToString(spdao.getInt(Constant.SP_BCRYPT_COST, 12), request.getParameter("password").toCharArray()); |
||||||
|
sendFailedResponse(response); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void sendFailedResponse(HttpServletResponse response) throws IOException{ |
||||||
|
response.setContentType("application/json"); |
||||||
|
JSONObject obj = new JSONObject(); |
||||||
|
obj.put("status", "failed"); |
||||||
|
obj.put("message", "Username and/or password incorrect, or account is being locked."); |
||||||
|
obj.write(response.getWriter()); |
||||||
|
} |
||||||
|
|
||||||
|
private void sendSuccessResponse(HttpServletResponse response) throws IOException{ |
||||||
|
response.setContentType("application/json"); |
||||||
|
JSONObject obj = new JSONObject(); |
||||||
|
obj.put("status", "success"); |
||||||
|
obj.write(response.getWriter()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,49 @@ |
|||||||
|
package org.leolo.web.dm.servlet; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import javax.servlet.ServletException; |
||||||
|
import javax.servlet.annotation.WebServlet; |
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
|
||||||
|
import org.json.JSONObject; |
||||||
|
import org.leolo.web.dm.Constant; |
||||||
|
import org.leolo.web.dm.dao.SystemParameterDao; |
||||||
|
|
||||||
|
/** |
||||||
|
* Servlet implementation class UserInfoServlet |
||||||
|
*/ |
||||||
|
@WebServlet("/UserInfo") |
||||||
|
public class UserInfoServlet extends BaseServlet { |
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
/** |
||||||
|
* @see BaseServlet#BaseServlet() |
||||||
|
*/ |
||||||
|
public UserInfoServlet() { |
||||||
|
super(); |
||||||
|
// TODO Auto-generated constructor stub
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) |
||||||
|
*/ |
||||||
|
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { |
||||||
|
super.doGet(request, response); |
||||||
|
if(this.fatalError) { |
||||||
|
return; |
||||||
|
} |
||||||
|
response.setContentType("application/json"); |
||||||
|
JSONObject obj = new JSONObject(); |
||||||
|
obj.put("status", "success"); |
||||||
|
obj.put("identified", this.userName!=null); |
||||||
|
if(this.userName!=null) { |
||||||
|
obj.put("username", this.userName); |
||||||
|
obj.put("method", identifiedByKey?"api-key":"session"); |
||||||
|
} |
||||||
|
obj.put("system-name", new SystemParameterDao().getString(Constant.SP_SYSNAME)); |
||||||
|
obj.put("api-version", Constant.SI_API_VERSION); |
||||||
|
obj.write(response.getWriter()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,29 @@ |
|||||||
|
package org.leolo.web.dm.util; |
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService; |
||||||
|
import java.util.concurrent.Executors; |
||||||
|
import java.util.concurrent.ThreadFactory; |
||||||
|
|
||||||
|
import org.leolo.web.dm.Constant; |
||||||
|
import org.leolo.web.dm.dao.SystemParameterDao; |
||||||
|
|
||||||
|
|
||||||
|
public class QueuedJobs { |
||||||
|
private static QueuedJobs instance; |
||||||
|
private ExecutorService pool = Executors.newFixedThreadPool(new SystemParameterDao().getInt(Constant.SP_QUEUED_JOB_THREAD, 10)); |
||||||
|
|
||||||
|
public static synchronized QueuedJobs getInstance() { |
||||||
|
if(instance==null) { |
||||||
|
instance=new QueuedJobs(); |
||||||
|
} |
||||||
|
return instance; |
||||||
|
} |
||||||
|
|
||||||
|
private QueuedJobs() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void queue(Runnable r) { |
||||||
|
pool.execute(r); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,25 @@ |
|||||||
|
package org.leolo.web.dm.util; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
|
||||||
|
public class ServletUtil { |
||||||
|
public static String getClientIpAddr(HttpServletRequest request) { |
||||||
|
String ip = request.getHeader("X-Forwarded-For"); |
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
||||||
|
ip = request.getHeader("Proxy-Client-IP"); |
||||||
|
} |
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
||||||
|
ip = request.getHeader("WL-Proxy-Client-IP"); |
||||||
|
} |
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
||||||
|
ip = request.getHeader("HTTP_CLIENT_IP"); |
||||||
|
} |
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
||||||
|
ip = request.getHeader("HTTP_X_FORWARDED_FOR"); |
||||||
|
} |
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
||||||
|
ip = request.getRemoteAddr(); |
||||||
|
} |
||||||
|
return ip; |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue