I practise Java EE 7 nowadays. I come across a problem when trying to authenticate a user by using container provided way, i.e., j_security_check .
Application server: Apache Tomcat 7
Project/Application name: ServletDrill
Resource (Servlet) annotated with #WebServlet
My Web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>ServletDrill</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<security-constraint>
<web-resource-collection>
<web-resource-name>To_Auth</web-resource-name>
<url-pattern>/auth/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>valid</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/FormAuth.jsp</form-login-page>
<form-error-page>/LogInErr.jsp</form-error-page>
</form-login-config>
</login-config>
</web-app>
My tomcat-users.xml:
<tomcat-users>
<role rolename="valid"/>
<user username="username" password="pass" roles="valid"/>
</tomcat-users>
My <form>:
<form action="/ServletDrill/j_security_check" accept-charset="UTF-8" method="post">
<fieldset id="postForm">
<legend>j_security_check method</legend>
<div class="partContainer">
<div class="left"><label for="user" >User Name: </label></div>
<div class="right"><input type="text" name="j_username" id="user" required="required" maxlength="20"></div>
</div>
<hr/>
<div class="partContainer">
<div class="left"><label for="pass" >Pass: </label></div>
<div class="right"><input type="password" name="j_password" id="pass" required="required" maxlength="20"></div>
</div>
<hr/>
<div class="partContainer">
<div class="left"><input type="submit" value="Log In"></div>
<div class="right"><input type="reset" value="Reset"></div>
</div>
</fieldset>
</form>
My protected resource (Servlet):
#WebServlet("/auth/NeedsPriorAuth")
public final class NeedsPriorAuth extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().append("Welcome, "+request.getRemoteUser()+". The user has been authenticated before hand").append("\n Auth Type: "+request.getAuthType());
}
}
When I execute the following link,
Access Protected Servlet
,the user redirected for authentication on the following page,
<form-login-page>/FormAuth.jsp</form-login-page>
(the <form> that I posted above).
Despite passing in correct credentials (posted above at tomcat-users.xml) the user redirected to the following Error page (posted above at Web.xml):
<form-error-page>/LogInErr.jsp</form-error-page>
What is the culprit which causes such an inconvenience for me? I've been stuck on this problem for several days now.
What about <realm>, do I need it?
Any ideas?
There you go, in the end, I have resolved the problem. The modification that I needed to apply:
<form action="j_security_check" accept-charset="UTF-8" method="post">
That is, the action has no application context prefix.
The other thing, is that there are, in my case, 2 instances of tomcat-users.xml file:
Under Apache Tomcat v7 directory (The server app)
And the one accessible from Project Explorer in Eclipse, under Servers2 name
When you modify the role(s), username, and password, if you want to see an affect to take place, modify it under the latter instance example; the server restart comes after.
Related
I have deployed my tomcat war succesfully on Amazon EC2 today.
On my local machine the following link: localhost:8080/login works totally fine
But on my EC2 instance, the link always has to have the .war name in the link:
http://some-amazon-link:8080/the-war-name/login
Now im struggling how and where i have to add that "/the-war-name/" in my Spring MVC project.
wether its in the
web.xml
the views
the controllers.
With the code below i can access the login with the following link:
localhost:8080/login
but i want it to be
localhost:8080/some-war-name/login
I've tried adding the /some-war-name/ to the #RequestMapping, changing the dispatcher
My login.jsp:
<html>
<head>
<title>Login ProV</title>
<link href="webjars/bootstrap/3.3.6/css/bootstrap.min.css"
rel="stylesheet">
</head>
<body>
<p><font color="red">${errorMessage}</font></p>
<form action="/login" method="POST">
<div class="form-group">
<label for="exampleInputEmail1">Benutzername</label>
<input name="name" type="text" class="form-control" id="exampleInputUsername1" placeholder="Benutzernamen eingeben">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input name="password" type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
</div>
<input class="btn btn-primary" type="submit" />
</form>
<script src="webjars/jquery/1.9.1/jquery.min.js"></script>
<script src="webjars/bootstrap/3.3.6/js/bootstrap.min.js"></script>
</body>
</html>
My web.xml
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>To do List</display-name>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/todo-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
My LoginController
package com.mschm.login;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import com.mschm.login.LoginService;
#Controller
#SessionAttributes("name")
public class LoginController {
#Autowired
LoginService service;
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String showLoginPage() {
return "login";
}
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String handleLoginRequest(#RequestParam String name, #RequestParam String password, ModelMap model) {
if(!service.validateUser(name, password)) {
model.put("errorMessage", "Invalid Credentials");
return "login";
} else {
model.put("name", name);
model.put("password", password);
return "welcome";
}
}
}
that "/the-war-name/"
is called the context path for your application.
You need to configure tomcat to use an empty context path in your case. There are several ways to achieve this.
One of them is to add following to $CATALINA_BASE/conf/server.xml:
<Host name="your.host.name" >
<Context path="" docBase="/path/to/the-war-name.war"/>
</Host>
Further Reading:
Defining a context in Apache Tomcat 8 Configuration Reference.
I am new in the servlet programming, so I want to ask how to call servlet from a html file.Basically I am using eclipse oxygen 3.0 for it, and already tried everything but did't success to call servlet from html.I also mention servlet in web.xml file as below
<servlet>
<servlet-name>ShowParameters</servlet-name>
<servlet-class>coreservlets.ShowParameters</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ShowParameters</servlet-name>
<url-pattern>/ShowParameters</url-pattern>
</servlet-mapping>
I am using coreservlets as a package.
There are two ways:
1) by using anchor tag:
<a href='ShowParameters?name=myname'> some url </a>
2) by using below form:
<form action="ShowParameters" method="post">
Name: <input maxlength="100" type="text" name="name" class="form-control" placeholder="" />
<input type="submit" value="submit">
</form>
To deploy the project I have purchased java server online with www.net4.in and created the database using phpmyadmin access given by them. I have deployed the project by transferring .war file of my project.
I have adjusted all the links like:
servlet path
form submission path
web.xml
all the required ones.
In servlet path if I add .java to servlet it is showing the code online, however the same path is giving error when I am submitting the form to Servlet.
<html>
<body>
<form id="theForm" name="form1" method="post" action="../../../db/src/com/mayuri/servlet/SelectCustServlet" enctype="multipart/form-data" target="_self" onSubmit="return verify()">
<fieldset class="login"><br>
<legend>Customer Details - Fill Customer Details</legend>
<div>
<label for="Ac_No">Account Number</label>
<input type="text" id="Ac_No" name="Ac_No" autocomplete="off" onKeyPress="return IsNumeric(event);" ondrop="return false;" onpaste="return false;" />
</div>
<div>
<label for="Center_No">Center Number</label> <input type="text" id="Center_No" autocomplete="off" class="input username" name="Center_No" onkeypress="return IsNumeric(event);" ondrop="return false;" onpaste="return false;" />
</div>
</fieldset><br>
<center>
<button type="submit" >Submit</button>
</center>
</form>
</body>
</html>
Servlet Code
package com.mayuri.servlet;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
/**
* Servlet implementation class FileUploadDBServlet
*/
//#WebServlet("/FileUploadDBServlet")
#MultipartConfig(maxFileSize = 10177215) // upload file's size up to 16MB
public class SelectCustServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* #see HttpServlet#HttpServlet()
*/
public SelectCustServlet() {
super();
}
private String dbURL = "jdbc:mysql://118.67.244.52/databasename";
private String dbUser = "loginid";
private String dbPass = "pwd";
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
Connection conn = null; // connection to the database
PrintWriter out = response.getWriter();
String message = null; // message will be sent back to client
try {
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
conn = DriverManager.getConnection(dbURL, dbUser, dbPass);
// gets values of text fields
String Ac_No = request.getParameter("Ac_No");
System.out.println("in Servlettttttttttttttttttttttttt" + Ac_No);
String Center_No = request.getParameter("Center_No");
try {
// connects to the database
// constructs SQL statement
String sql = "INSERT INTO cust (Ac_No, Center_No ) values (?,?)";
PreparedStatement statement = conn.prepareStatement(sql);
statement.setString(1, Ac_No);
statement.setString(2, Center_No);
out.println("dfsfsdf");
// sends the statement to the database server
int row = statement.executeUpdate();
if (row > 0) {
// message = "File uploaded and saved into database";
}
} catch (SQLException ex) {
message = "ERROR: " + ex.getMessage();
ex.printStackTrace();
}
} catch (SQLException ex) {
message = "ERROR: " + ex.getMessage();
ex.printStackTrace();
} finally {
if (conn != null) {
// closes the database connection
try {
conn.close();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
// sets the message in request scope
// request.setAttribute("Message", message);
// forwards to the message page
//request.getRequestDispatcher("Message.jsp").forward(request, response);
}
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>softwarematcs</display-name>
<servlet>
<description></description>
<display-name>SelectCustServlet</display-name>
<servlet-name>SelectCustServlet</servlet-name>
<servlet-class>db.src.com.mayuri.servlet.SelectCustServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SelectCustServlet</servlet-name>
<url-pattern>../../src/com/mayuri/servlet/SelectCustServlet</url-pattern>
</servlet-mapping>
</web-app>
Use short and valid Servlet URL pattern and don't forget to prefix form action with context root path. Servlet URL pattern shows in the browser URL.
Either use JSP Expression Language ${pageContext.request.contextPath} or core tag library <c:url> for getting context root relative URL path.
For example:
JSP:
<form action="${pageContext.request.contextPath}/myServlet" method="post">
web.xml:
<servlet>
<servlet-name>SelectCustServlet</servlet-name>
<servlet-class>db.src.com.mayuri.servlet.SelectCustServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SelectCustServlet</servlet-name>
<url-pattern>/myServlet</url-pattern>
</servlet-mapping>
Read more...
Try the following pieces of code. I've modified the web.xml to the correct servlet-class name com.mayuri.servlet.SelectCustServlet and the servlet's url-pattern to /SelectCustServlet.
Also, I've updated the HTML form's action to the correct servlet url pattern.
HTML form:
<html>
<body>
<form id="theForm" name="form1" method="post" action="<%=request.getContextPath()%>/SelectCustServlet" enctype="multipart/form-data" target="_self" onSubmit="return verify()">
<fieldset class="login"><br>
<legend>Customer Details - Fill Customer Details</legend>
<div>
<label for="Ac_No">Account Number</label>
<input type="text" id="Ac_No" name="Ac_No" autocomplete="off" onKeyPress="return IsNumeric(event);" ondrop="return false;" onpaste="return false;" />
</div>
<div>
<label for="Center_No">Center Number</label> <input type="text" id="Center_No" autocomplete="off" class="input username" name="Center_No" onkeypress="return IsNumeric(event);" ondrop="return false;" onpaste="return false;" />
</div>
</fieldset><br>
<center>
<button type="submit" >Submit</button>
</center>
</form>
</body>
</html>
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>softwarematcs</display-name>
<servlet>
<description></description>
<display-name>SelectCustServlet</display-name>
<servlet-name>SelectCustServlet</servlet-name>
<servlet-class>com.mayuri.servlet.SelectCustServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SelectCustServlet</servlet-name>
<url-pattern>/SelectCustServlet</url-pattern>
</servlet-mapping>
</web-app>
I am trying to build a Spring based web application and I would like to start with configuring a simple authentication system based on username & password tuples stored in a database table.
It is my understanding that this can be easily achieved using Spring security, but I cannot get it to work.
The following is my web.xml file.
<?xml version="1.0" encoding="UTF-8"?>
<web-app
version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/Servlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Servlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Follows the servlet-context.xml file. The bob and sam users are there for testing purposes. After I get this right I will switch to a JDBC based user service.
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<sec:http use-expressions="true">
<sec:intercept-url pattern="/**" access="permitAll" />
<sec:form-login
login-page="/home.html"
login-processing-url="/j_spring_security_check"
authentication-failure-url="/login-error.html"
default-target-url="/welcome.html" />
<sec:logout logout-success-url="/home.html" />
</sec:http>
<sec:authentication-manager>
<sec:authentication-provider>
<sec:password-encoder hash="md5"/>
<sec:user-service>
<sec:user name="bob" password="12b141f35d58b8b3a46eea65e6ac179e" authorities="ROLE_SUPERVISOR, ROLE_USER" />
<sec:user name="sam" password="d1a5e26d0558c455d386085fad77d427" authorities="ROLE_USER" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
<context:component-scan base-package="cz.dusanrychnovsky.whattoreadnext" />
<mvc:annotation-driven />
</beans>
This is my Home controller.
#Controller
public class HomeController
{
#RequestMapping(value = "/home.html")
public String home() {
return "home";
}
#RequestMapping(value = "/login-error.html")
public String loginError(Model model) {
model.addAttribute("loginError", true);
return "home";
}
}
And this is my thymeleaf based view.
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring3-3.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Contacts</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<div id="content">
<h1>Welcome to the site!</h1>
<p th:if="${loginError}">Wrong user or password</p>
<form th:action="#{/j_spring_security_check}" method="post">
<label for="j_username">Email address</label>:
<input type="text" id="j_username" name="j_username" /> <br />
<label for="j_password">Password</label>:
<input type="password" id="j_password" name="j_password" /> <br />
<input type="submit" value="Log in" />
</form>
</div>
</body>
</html>
When I deploy the WAR file to my local Tomcat installation and visit the http://localhost:8080/test/home.html URL, the home page opens fine. When I fill in the form, though, which gets submitted to http://localhost:8080/test/j_spring_security_check, I get a 404 - The requested resource () is not available. error.
What am I doing wrong? Please bear with me as I'm a newcomer to both Spring MVC/Security and Thymeleaf.
You need to configure Spring Security filter in web.xml
You cannot configure Spring Security in servlet-context.xml, because servlet-context.xml belongs to specific DispatcherServlet, but Spring Security filter works before request reaches any servlet.
You need to create a root application context using ContextLoaderListener and put Spring Security configuration there.
Actually, as long as you don't need separate servlet-context.xml and applicationContext.xml, I'd suggest you to move everything from servlet-context.xml to applicationContext.xml and leave servlet-context.xml effectively empty (that is, leave its <beans> element empty).
I've got a login form rendered by ThymeLeaf and powered by Spring Security. If an authentication error occurs, I'd like the username field to be pre-populated with the value the user entered on their previous attempt. Spring Security provides SPRING_SECURITY_LAST_USERNAME for that purpose, but my searches of the documentation and online don't turn up how to expose that via ThymeLeaf. Below are simplified versions of the relevant files:
My security XML file:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<sec:http auto-config="true">
<sec:intercept-url pattern="/login/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<sec:form-login login-page="/login" authentication-failure-url="/login/fail" default-target-url="/"/>
<sec:logout />
</sec:http>
<sec:global-method-security secured-annotations="enabled"/>
</beans>
login.html:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head th:include="template::head"></head>
<body class="login">
<nav th:include="template::nav"></nav>
<div>
<h2>Log in</h2>
<div>
<div th:if="${loginFail}" class="error">
Username/password incorrect.
</div>
<form id="login" th:action="#{/j_spring_security_check}" method="POST">
<dl>
<dt>Username</dt>
<dd><input type="text" id="j_username" name="j_username" />#mshare.net</dd>
<dt>Password</dt>
<dd><input type="password" id="j_password" name="j_password" /></dd>
</dl>
<input type="submit" value="Log in" />
</form>
</div>
</div>
</body>
</html>
Controller error handler:
#RequestMapping(value = "/login/fail")
public String loginError(Model model) {
model.addAttribute("loginFail", Boolean.TRUE);
return "login";
}