Spring Security - Custom login form not calling the AuthenticationProvider on submit - spring

RESOLVED:
Spring Security 4 no longer seems to provide a default login-processing-url. We must now explicitly provide it in form-login as follows.
login-processing-url="/j_spring_security_check"
I have a strange combination of behaviors that has me stumped. Found lots of tutorials and similar questions but none quite exactly like this. I hope someone can help.
Short version: The login page redisplays - regardless of good or bad username/password and the AuthenticationProvider is never called (breakpoints not tripped).
Long version:
Step 1 - successful
I was able to use spring security with the default AuthenticationProvider and the default auto-generated login screen. Good.
I was able to write a custom AuthenticationProvider and UserDetailsService. Things worked fine, using the default auto-generated login screen. Good.
... so the above rules out a lot of possible problems.
Step 2 - the problem - Setting up to use a custom login form
1) I added the form-login:
<sec:form-login
login-processing-url="/j_spring_security_check"
login-page="/login.htm"
authentication-failure-url="/login.htm?error"
username-parameter="username"
password-parameter="password"
/>
<sec:logout logout-success-url="/login.htm?logout"/>
<sec:csrf/>
2) I made sure that login URLs will be accessible:
<sec:intercept-url pattern="/login.htm" access="permitAll" />
3) I created a trivial LoginController:
#Controller
public class LoginController {
#RequestMapping(value="/login.htm")
public ModelAndView login(HttpServletRequest request,
#RequestParam(value="error", required=false) String error,
#RequestParam(value="logout",required=false) String logout) {
ModelAndView model = new ModelAndView();
if(error!=null) {
model.addObject("error", "Invalid username or password.");
}
if(logout!= null) {
model.addObject("msg", "Successfully logged out.");
}
model.setViewName("/login");
return model;
}
4) I created a trivial login.jsp:
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Login</title>
</head>
<body onload="document.loginForm.username.focus()">
<h1>Login form</h1>
<div>
<h2>Username and Password</h2>
<c:if test="${not empty error}">
<div style="color: red">${error}</div>
</c:if>
<c:if test="${not empty msg}">
<div style="color: green">${msg}</div>
</c:if>
<form name="loginForm" action="<c:url value='j_spring_security_check' />" method='POST'>
<table>
<tr>
<td>User:</td>
<td><input type='text' name='username' value = '' /></td>
</tr>
<tr>
<td>Password:</td>
<td><input type='password' name='password' /></td>
</tr>
<tr>
<td colspan='2'><input name="submit" type="submit" value="submit" /></td>
</tr>
<tr><td colspan='2'>[${not empty webUser}]</td></tr>
</table>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
</div>
</body>
</html>
5) Then I setup some breakpoints in Eclipse to see how it behaves.
The login page displays fine.
I enter username and password and hit submit.
Good or bad username and password all cause the same behavior, the redisplay of the login page.
The breakpoints in the AuthenticationProvider are never tripped.
My interpretation is that the action specified by < c : url value='j_spring_security_check' /> is not triggering the AuthenticationProvider.
Note that the very same custom AuthenticationProvider worked flawlessly when I used the built-in default spring security login form. That tells me that the problem has nothing to do with the provider itself, but rather with "reaching it" when I hit submit in login.jsp, especially since I have a breakpoint on its very first line and it is not being tripped.
I bet my error is trivial and obvious to someone familiar with Spring Security. My thanks in advance to anyone that catches it.

This question is resolved.
Spring Security 4 no longer seems to provide a default login-processing-url. We must now explicitly provide it in form-login as follows.
login-processing-url="/j_spring_security_check"
After banging my head against the wall for quite a few hours I stumbled upon the solution. I hope this is of help to others in the same situation.

Related

Bean property 'principal.username' is not readable or has an invalid getter method, when I logout and try to go to student/list

When I login using spring security, I am redirected to student/list but once I log out and try to access link again I get
type=Internal Server Error, status=500
Invalid property 'principal.username
It is not finding valid username, but I need to access the login page again when I am logged out, this is my jsp code
<form:form action="${contextPath}/authenticateTheUser" method="POST">
<c:if test="${param.error !=null}">
<i class="failed">Sorry! You have entered ivalid username/password</i>
</c:if>
<c:if test="${param.logout !=null}">
<i class="logout">You Have Been Logged Out</i>
</c:if>
<table>
<tr>
<td><label>User Name</label></td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>Password</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td><input type="submit" name="Login"></td>
</tr>
</table>
</form:form>
logout fuctionality has been added in my list-student but once I am logged out I type in my credentials and it gives me white label 404 but when I go to student/list after I get this error I am logged in as the correct user, here is my list-student page
<form:form action="${contextPath}/logout" method="POST">
<input type="submit" value="Log Out">
</form:form>
the contextPath refers to my configure code
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/student").hasRole("EMPLOYEE").antMatchers("/student/showFormForAdd/**")
.hasRole("ADMIN").antMatchers("/student/addAddress/**/student/showFormForAdd/**").hasRole("MANAGER")
.and().formLogin().loginPage("/login").loginProcessingUrl("/authenticateTheUser").permitAll().and()
.logout().permitAll();
}
Overall I need to be able to login and directly be redirected to my student-list, and if I logout and login again I need to have the same functionality, not get a white label 500 or a Bean property 'principal.username' is not readable here is my controller code
#Controller
#RequestMapping("/student")
public class StudentController {
#GetMapping("/list")
public String listStudents(Model model) {
List<Student> students = studentService.showAllStudents();
model.addAttribute("students", students);
return "list-students";
}
}
my login mapping
#Controller
public class LoginController {
#RequestMapping("/login")
public String showMyLoginPage() {
return "login-form";
}
}
note my login controller has a default to /login but my /list for my list-students has a request mapping at class level named /student

Login page does not show logout message how is expected

I am working with Spring Security version 4.2.3.RELEASE
Use csrf is mandatory.
I used how reference the following for the login.jsp file, 5.3 Java Configuration and Form Login, thus I have:
<body>
<spring:url var="loginUrl" value="/perform_/login"/>
<form name="login" action="${loginUrl}" method="POST">
<fieldset class="fieldset">
<legend><spring:message code="login.legend"/></legend>
<c:if test="${param.error != null}">
<p class="error"><spring:message code="login.invalid"/></p>
</c:if>
<c:if test="${param.logout != null}">
<p><spring:message code="logout.valid"/></p>
</c:if>
<table>
.... fields for username and password and submit button ...
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
in other .jsp page to logout purposes exists:
<spring:url var="logoutUrl" value="/perform/logout" />
<form id="logoutForm" action="${logoutUrl}" method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<spring:message code="logout.title"/>
</form>
<script>
function formSubmit() {
document.getElementById("logoutForm").submit();
}
</script>
Finally through Javaconfig for Spring Security the following exists:
.formLogin()
.loginPage("/perfom/login")
.loginProcessingUrl("/perform_/login")
.usernameParameter("username")//default
.passwordParameter("password")//default
.defaultSuccessUrl("/welcome")
.failureUrl("/perfom/login?error") //default is /login?error
.permitAll()
.and()
.logout()
.logoutUrl("/perform/logout")//default is /logout
.logoutSuccessUrl("/perfom/login?logout")//default is /login?logout
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID");
Note: /perfom/login is used in:
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/message").setViewName("general/message");
registry.addViewController("/welcome").setViewName("general/welcome");
registry.addViewController("/perfom/login").setViewName("login/login");
}
When the login page is loaded and either username or password values are incorrects the same login page is loaded again and the error message appears. Therefore <c:if test="${param.error != null}"> works fine.
The problem is when the logout event happens it goes to the login.jsp page how is expected but the logout message does not appear. But if in that moment I try to do a login with valid values the same login.jsp page is loaded again and just then the logout message appears.
I did a research and 'seems' correct my configuration, even according with this:
How to get the logout message in custom login page
What missing configuration is need it?
TL; DR You need to add another permitAll() call after deleteCookies("JSESSIONID").
As per documentation permitAll() for FormLoginConfigurer
Ensures the urls for failureUrl(String) as well as for the HttpSecurityBuilder, the getLoginPage and getLoginProcessingUrl are granted access to any user.
But you need to grant access to logoutSuccessUrl as well (to be fair access to logoutUrl is also needed but prerequisite for logout is that user is authenticated).
So what happens in your case is this:
You perform logout and get redirect to logoutSuccessUrl in response;
You dont have access to logoutSuccessUrl so normal process kicks in: url is saved to session and you get another redirect to login page (without parameter);
When you perform login you get redirect to saved logoutSuccessUrl and finally see your message.

Pre-populate the Form field by formBackingObject method using standard html code in spring web

I just trying to pre-populate the form field in user form by formBackingObject.
I am using simple html code not spring tags to create the field.
Actually I'v tried with spring tag but spring tags for input filed is not working so I moved to simple html code but spring tags works for <form:form action=**> but not for <form:input path="name" />
I'v check taglib import segment but its fine and I don't understand if <form:form />
is working then why <form:input path="name" /> is not working.
I'm describing this because somewhere and someone from StackOverflow said that for getting benefit of formBackingObject, I'v to use spring tags for input field but in my case its not working.
Here I'm considering of formBackingObject only for pre-populating form field that what my question title suggest. Anyhow I'v to do it soon.
I'v gone through about 30-40 page link for finding of this solution but .....
I need a very simple example of it where jsp field getting value of object that is return by formBackingObject
I'm not familiar with annotation
Here is my tried and failed example
contact_form.jsp
<form:form action="edit.htm" commandName="contact" method="POST">
Name:<input type="text" name="name" value=${contact.name} /><br>
Address:<input type="text" name="address" value=${contact.address}/><br>
<input type="submit" value="Submit"/><br>
EditController
public class EditController extends SimpleFormController {
private static Contact cont = new Contact();
static {
cont.setAddress("aaa");
cont.setName("bbb");
}
#Override
protected void doSubmitAction(Object command) throws Exception {
System.out.println("In do submit method");
}
#Override
protected Object formBackingObject(HttpServletRequest request)
throws Exception {
System.out.println("In FormBackingObject");
return cont;
}
spring-servlet.xml
<bean id="/edit.htm" class="tryPack7.EditController">
<property name="CommandClass" value="tryPack7.Contact" />
<property name="commandName" value="contact" />
<property name="formView" value="contact_form" />
<property name="successView" value="success" />
</bean>
Thanks in advance
Assuming you have specified handler mapping in your confog xml file:
Make sure you have jsp on similar lines as below:
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<head>
<title>Sample Page</title>
</head>
<body>
<form:form commandName="contact" method="POST">
<table>
<tr >
<td>Name:<form:input path="name"/></td>
</tr>
<tr >
<td>Address:<form:input path="address"/></td>
</tr>
<tr>
<td colspan="3"><input type="submit"/></td>
</tr>
</table>
</form:form>
</body>
</html>
Also, rather than using static way, I would suggest to initialize contact form in formBackingObject as shown below:
public class EditController extends SimpleFormController {
public ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
Contact contact=(Contact)formBackingObject(request);
return new ModelAndView("contact_form","contact",contact);
}
public Object formBackingObject(HttpServletRequest req)
{
Contact contact = new Contact();
contact.setAddress("aaa");
contact.setName("bbb");
return contact;
}
}

custom authentication using only authorities by username query in spring security

The following code
<jdbc-user-service data-source-ref="dataSource"
authorities-by-username-query="select USERNAME,ROLE from USERS where USERNAME=?"/>
</authentication-provider>
gives me error
org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
Jan 14, 2014 10:17:40 PM org.springframework.jdbc.support.SQLErrorCodesFactory <init>
INFO: SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase]
I recommend implementing the interface UserDetails, you can check out this project:
https://github.com/pgardunoc/spring-security
The idea is to implement the interface and you will have more control.
Spring Form .I want to update the request made by customer by changing its status.some of field coming form controller .I want to submit form to controller in order to acess the values
h2>Update Request</h2>
>form:form method="POST" action="/my/update.htm" commandName="servicehistory">
table align="center" border="0">
<tr><td>Request Id: </td>
<td><form:input path="sr.ServiceRequestId" value="${sr.serviceRequestId}"></form:input> < other fields..............
<tr><td><input type="submit" value="Save" /></td></tr>
Controller which access the form value and update the request in database.but it is not happening.GUI displays resource is not available.It was working without Spring Security.
#RequestMapping(value="/my/update.htm",method=RequestMethod.POST)
public ModelAndView update(#ModelAttribute("servicehistory") #Valid ServiceHistories sh1,BindingResult result,ModelMap map)
{
Redirect it to another page after validating the form
return new ModelAndView(("redirect:/displaylist.htm"));
}
>Spring Security which implement security after matching url and user role
< http auto-config="true" use-expressions="true">
<intercept-url pattern="/admin/*" access="hasRole('Technician')" />
<intercept-url pattern="/my/**" access="hasRole('Techincian')" />
>This is JSp page that is used to assign the compalint to techincian. After hitting submit, it give number format exception
<c:forEach items="${hlist}" var="history" varStatus="i">
<tr>
<td><form:input path="requests[${i.index}].ServiceRequestId" value="${history.serviceRequestId }"></form:input></td>
<td><form:input path="requests[${i.index}].Description" value="${history.description }"></form:input></td>
<td><form:select multiple="false" path="requests[${i.index}].userto.UserId">
<form:option value="NONE" label="--- Select ---" />
<c:forEach items="${user}" var="u" varStatus="index">
<form:option label="${u.userName}" value="${u.userId}" />
</c:forEach>
</form:select>
</tr>
</c:forEach>
<tr><td> <input type="submit" value="Assign Technicians"></td></tr>
>Controller : I am using Autopopulating list to access list coming from above form.But it gives Number format exception because label contains String value and value attribute is an int. But it is working for another scenario which is identical to this one.
#ModelAttribute("requestForm")
public RequestForm getRequest()
{
RequestForm rf=new RequestForm();
rf.setRequests(new AutoPopulatingList(ServiceRequests.class));
return rf;
}
#RequestMapping(value="/admin/assigntech.htm",method=RequestMethod.POST)
public String assign(#ModelAttribute("requestForm") RequestForm requestForm,BindingResult result,ModelMap map)
{
List<ServiceRequests> sr=(List<ServiceRequests>)requestForm.getRequests();
System.out.println(sr.size());
System.out.println(sr.get(0).getUserto().getUserId());
return "Welcome";
}

How can I display error in JSP without a <form:form>

I have a jsp page with a List<Object> as the #ModelAttribute. However, there are no <form:form> tags in the page. All I'm doing is print the contents of the List.
In my Controller.java, I'm binding an error by doing:
result.rejectValue("", "NOT_LOGGED_IN", "You should Login first") ;
But since I dont have a form in my jsp, I'm not able to access the error with:
<form:errors path="" /> <br/>
Please tell me how to access the error (or what I'm doing wrong).
In your controller:
model.addAttribute("errors", result.getAllErrors());
In your JSP:
<c:forEach items="${errors}" var="error">
<%-- do want you want with ${error} --%>
<c:out value="${error.defaultMessage}" />
</c:forEach>
Associate global errors this way:
result.reject("NOT_LOGGED_IN", "You should Login first") ;
You can show the global errors in the jsp :
<form:errors cssClass="error" delimiter="<p/>" />
for any specific error
set in your code like-
model.addObject("errorMsg","username/password failed");
And show this error on jsp in this way:
<c:out value="${errorMsg}"/>
This way you would get your error on jsp.
there's a way to reference the error like
instead of
<form:form commandName="object">
<form:errors path="field"/>
</form:form>
best
Check if Expression Language evaluation is enabled in JSP(By default it is disabled).If not add below code to enable it.
<%# page isELIgnored="false" %>

Resources