Browse Source

Basic login/logout

- pending issue: header info box not updated right after login.
feature/jQueryMainPage
LO Kam Tao Leo 4 years ago
parent
commit
cba8828784
  1. 3
      WebContent/META-INF/context.xml
  2. 1
      WebContent/WEB-INF/undertow-handlers.conf
  3. 18
      WebContent/index-old.jsp
  4. 114
      WebContent/index.jsp
  5. 9
      src/org/leolo/web/dm/Constant.java
  6. 82
      src/org/leolo/web/dm/dao/UserDao.java
  7. 37
      src/org/leolo/web/dm/model/User.java
  8. 20
      src/org/leolo/web/dm/servlet/BaseServlet.java
  9. 96
      src/org/leolo/web/dm/servlet/LoginServlet.java
  10. 49
      src/org/leolo/web/dm/servlet/UserInfoServlet.java
  11. 29
      src/org/leolo/web/dm/util/QueuedJobs.java
  12. 25
      src/org/leolo/web/dm/util/ServletUtil.java
  13. 5
      src/org/leolo/web/dm/util/SystemInformation.java

3
WebContent/META-INF/context.xml

@ -0,0 +1,3 @@
<Context>
<CookieProcessor sameSiteCookies="strict" />
</Context>

1
WebContent/WEB-INF/undertow-handlers.conf

@ -0,0 +1 @@
samesite-cookie(mode=Lax)

18
WebContent/index-old.jsp

@ -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>

114
WebContent/index.jsp

@ -1,18 +1,100 @@
<!DOCTYPE html><%@
page import="java.util.Collection,org.leolo.web.dm.dao.*,org.leolo.web.dm.model.*,org.leolo.web.dm.util.*"
%><html>
<head>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<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>
<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>
<body>
<nav>
Download Manager - <span id="sysname"></span>
<span class="status" id="hdr-action">Login</span>
<div class="hidden hdr-info-box ui-corner-all" id="hdr-uinfo">
<button id="hdr-uinfo-logout">Logout</button>
</div>
<div class="hidden hdr-info-box ui-corner-all" id="hdr-login">
<div class="hidden ui-state-error ui-corner-all" style="padding:0.3em;" id="hdr-login-msg">
<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>
</html>

9
src/org/leolo/web/dm/Constant.java

@ -8,11 +8,20 @@ public class Constant {
//Basic user information map
public static final String BUI_KEY_USER_ID = "uid";
public static final String BUI_KEY_USER_NAME = "uname";
public static final String BUI_KEY_PASSWORD = "passwd";
public static final String BUI_KEY_FAIL_CNT = "fail_cnt";
//System parameter map
public static final String SP_DATA_DIR = "data_dir";
public static final String SP_CACHE_DIR = "cache_dir";
public static final String SP_MAX_CACHE_SIZE = "cache_size";
public static final String SP_SYSNAME = "sys_name";
public static final String SP_MAX_LOGIN_FAIL_CNT = "max_fail_login";
public static final String SP_BCRYPT_COST = "bcrypt_cost";
public static final String SP_QUEUED_JOB_THREAD = "qj_thread";
//System Information
public static final String SI_API_VERSION = "0.1";
//Common value
public static final int COM_DEFAULT_USER_ID = 1;

82
src/org/leolo/web/dm/dao/UserDao.java

@ -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);
}
}
}

37
src/org/leolo/web/dm/model/User.java

@ -1,5 +1,42 @@
package org.leolo.web.dm.model;
import java.util.HashSet;
import java.util.Set;
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;
}
}

20
src/org/leolo/web/dm/servlet/BaseServlet.java

@ -25,6 +25,7 @@ public class BaseServlet extends HttpServlet {
protected String userName = null;
protected int userId = Constant.COM_DEFAULT_USER_ID;
protected boolean fatalError = false;
protected boolean identifiedByKey = false;
/**
* @see HttpServlet#HttpServlet()
@ -34,17 +35,21 @@ public class BaseServlet extends HttpServlet {
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.info("Request - [GET] {}", request.getRequestURI());
//Reset parameters
private void resetParam() {
userName = null;
userId = Constant.COM_DEFAULT_USER_ID;
fatalError = false;
identifiedByKey = false;
}
if(request.getAttribute(Constant.SESSION_USER_NAME)!=null) {
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// log.info("Request - [GET] {}", request.getRequestURI());
resetParam();
// log.info("SUN : {}", request.getSession().getAttribute(Constant.SESSION_USER_NAME));
if(request.getSession().getAttribute(Constant.SESSION_USER_NAME)!=null) {
userName = request.getSession().getAttribute(Constant.SESSION_USER_NAME).toString();
userId = (Integer) request.getSession().getAttribute(Constant.SESSION_USER_ID);
}
@ -56,6 +61,7 @@ public class BaseServlet extends HttpServlet {
if(buim!=null) {
userName = buim.get(Constant.BUI_KEY_USER_NAME).toString();
userId = (Integer) buim.get(Constant.BUI_KEY_USER_ID);
identifiedByKey = true;
apiDao.markKeyUsed(key);
}else {
response.setContentType("application/json");

96
src/org/leolo/web/dm/servlet/LoginServlet.java

@ -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());
}
}

49
src/org/leolo/web/dm/servlet/UserInfoServlet.java

@ -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());
}
}

29
src/org/leolo/web/dm/util/QueuedJobs.java

@ -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);
}
}

25
src/org/leolo/web/dm/util/ServletUtil.java

@ -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;
}
}

5
src/org/leolo/web/dm/util/SystemInformation.java

@ -0,0 +1,5 @@
package org.leolo.web.dm.util;
public class SystemInformation {
}
Loading…
Cancel
Save