Login with Spring security using Ajax - spring

I am trying to work on Ajax based login system using Spring Security and it was working fine, till I got with another requirement where I need to configure authentication-success-handler, so as I can do some post processing once user is authenticated by Spring security.
Earlier I was not using <form-login/> for showing Login page.My Login section is a drop down and part of header section which is common for entire application.
This is how I am trying to configure Spring security
<http pattern="/customer/**" auto-config="true" use-expressions="true" authentication-manager-ref="customerAuthenticationManager">
<!--<http pattern="/customer/**" auto-config="true" use-expressions="true">-->
<intercept-url pattern="/customer/logon.html*" access="permitAll" />
<intercept-url pattern="/customer/denied.html" access="permitAll"/>
<intercept-url pattern="/customer" access="hasRole('AUTH_CUSTOMER')" />
<intercept-url pattern="/customer/" access="hasRole('AUTH_CUSTOMER')" />
<intercept-url pattern="/customer/*.html" access="hasRole('AUTH_CUSTOMER')" />
<intercept-url pattern="/customer/*/*.html" access="hasRole('AUTH_CUSTOMER')" />
<form-login login-processing-url="/customer/logon.html" login-page="/shop/home.html"
authentication-success-handler-ref="webshopAuthenticationSuccessHandler" />
<logout invalidate-session="true"
logout-success-url="/customer/home.html"
logout-url="/customer/j_spring_security_logout" />
<access-denied-handler error-page="/customer/denied.html"/>
</http>
With these configuration, when ever I am clicking on login button with correct credentials, i am getting HTTP 302 error
http://localhost:8080/shop/customer/logon.html 302 found
Not sure what exactly I am doing wrong?
<form id="login" method="post" accept-charset="UTF-8">
<input id="userName" type="text" name="userName" size="30" />
<button type="submit" class="btn">Login</button>
</form>
Ajax Code
var data = $(this).serializeObject();
$.ajax({
'type': 'POST',
'url': "<c:url value="/customer/logon.html"/>",
'contentType': 'application/json',
'data': JSON.stringify(data),
'dataType': 'json',
....
}}:
Login Section
Authentication authenticationToken = new UsernamePasswordAuthenticationToken(customer.getUserName(), customer.getPassword());
try {
Authentication authentication = customerAuthenticationManager.authenticate(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
resp.setStatus(AjaxResponse.RESPONSE_STATUS_SUCCESS);
} catch (AuthenticationException ex) {
resp.setStatus(AjaxResponse.RESPONSE_STATUS_FAIURE);
}
Any inputs? With all my efforts, I am still getting 302 and with above configuration, even my logon.html controller is not getting called.
My main issue is when I am enabling Spring security using
<form-login login-processing-url="/customer/logon.html" login-page="/shop/home.html"
authentication-success-handler-ref="webshopAuthenticationSuccessHandler" />
I am getting 302 from logon.html and even logon.html Controller is not being triggered (placed debugger on it)

First you don't need a controller, spring security handles all this for you. So drop the controller.
The current problem is due to your JSON based post. This is handled by the normal filter which handles login, but it doesn't see a j_username and j_password field and as such will redirect (the 302) to the login page (in your case /shop/home.html) asking for those fields.
Basically you are posting the data as JSON whereas it simply should be just an ajaxified form post. This allows you to write a simple AuthenticationSuccessHandler which simply returns a 401 or a 200 status-code (and maybe a body) when things are ok.
Changing your ajax submit to something like this should make things work.
var data = $(this).serializeObject();
$.ajax({
'type': $(this).action,
'url': $(this).target,
'data': data
}):
This should create a ajax form post. Might be that you need a call to preventDefault() in the surrounding method to stop the form from actual posting.

With the login form that is currently posted it cannot work, as a form like this is needed:
<body>
<form action="/j_spring_security_check" method="POST">
<label for="username">User Name:</label>
<input id="username" name="j_username" type="text"/>
<label for="password">Password:</label>
<input id="password" name="j_password" type="password"/>
<input type="submit" value="Log In"/>
</form>
</body>
The post needs to be done j_spring_security_check, and username and password need to be suplied in fields j_username and j_password, otherwise it won't work.
I think you are getting a redirect to a login page.
You can see where the redirect is going with the chrome debugger or firebug if using firefox.
To approach this first get the rest of the config working with the default login page generated by spring security, by removing login-page="/customer/logon.html".
Once this works you can add back the custom login page, but it needs to have the elements above.
Also I believe you are trying to post a JSTL tag via ajax to the server? If it's true it won't work, otherwise can you edit the question.
Try it step by step by first using the default login page, then with an ajax request with hardcoded values just for testing and then when this works with a custom login page.

response code 302 is not an error indication. It's a way of performing a redirection.
as you have not shared your webshopAuthenticationSuccessHandler code.
But i am pretty much sure you are redirecting the request to some specified resource after processing some certain condition. so just check your header and get the URL of redirection.
and then redirect with javascript code.
An HTTP response with this status code will additionally provide a URL in the Location header field.
For an example -
Client request:
GET /index.html HTTP/1.1
Host: www.example.com
Server response:
HTTP/1.1 302 Found
Location: http://www.iana.org/domains/example/
so along with this error code you are getting http://www.iana.org/domains/example/ url where your authenticationSuccessHandler want you to redirect so redirect it through javascript code.

Related

How to use form login with Rest Assured with Spring MVC/Thymeleaf?

I have a Spring Boot 2.1.4 application using Thymeleaf with a fairly standard login form:
<form class="form-horizontal" th:action="#{/login}" method="post">
<input type="text" class="form-control" name="username" th:placeholder="#{username}"/>
<input type="password" class="form-control" name="password" th:placeholder="#{password}"/>
<button type="submit" class="btn btn-default btn-md action-button-primary" th:text="#{login}"></button>
</form>
When starting the application, this all works fine and I can log on.
I now want to write an integration test using Rest Assured that accesses a secured page.
given().auth()
.form("admin", "admin",
new FormAuthConfig("/login", "username", "password")
.withLoggingEnabled()
.withAutoDetectionOfCsrf())
.when()
.get("/people")
.then()
.log()
.all().assertThat().statusCode(HttpStatus.SC_OK);
I see in the logging that a POST request is done:
Request method: POST
Request URI: http://localhost:58352/login
Proxy: <none>
Request params: <none>
Query params: <none>
Form params: username=admin
password=admin
_csrf=2296f8a3-39a5-48b2-99ec-a20ed01cfeec
Path params: <none>
Headers: Accept=*/*
Content-Type=application/x-www-form-urlencoded; charset=ISO-8859-1
However, Rest Assured never ends up at the /people page. It seems it gets redirected to /login again.
If I enable the Spring Security debug logging, I see:
CsrfFilter - Invalid CSRF token found for http://localhost:58352/login
My assumption is that Rest Assured is somehow using the wrong CSRF token? What am I doing wrong?

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.

Get data in Angular 2 from Spring

Let's say that I have a page rendered by Spring Framework. It is a page with Angular 2 code. How could I receive data on the page if Spring sends it? I do not want to render a page, then do an HTTP request, then get a response. No, in Spring controller I want to send data and somehow receive it with Angular 2 and show (without additional requests). With JSPs it is obvious how to do, but how to set Angular 2 model from JSON sent by Spring upon rendering the page?
What I have right now is Spring Security managing login. Then, in controller data gets prepared depending on the user, and then I want to send it to Angular 2 page. So, I am stuck with not knowing how to render a page AND attach data for it to be rendered by Angular 2 at the same time.
I'm not sure if I totally understood the question... anyway if I did, I'd do in this way
In spring controller, before rendering the JSP page, i'd all the business logic you need to create the JSON; then I'd create a JSON string and I'd attach it to the model
It means I'd do something like that:
#RequestMapping("/render")
public String renderView(ModelAndView model)
{
//Your JSON String
String json = "{\"foo\":\"bar\"}";
model.addAttribute("json", json);
return "yourJspName";
}
Then in your JSP you can use Angular to access to the json contained in the model (that is in the pageContext); for example, by using JSTL:
<script type="text/javascript">
var theJsonString = '${json}';
</script>
or
<script type="text/javascript">
var theJsonString = '<c:out value="${json}" />';
</script>
At this point you have the JS variable named theJsonString with your JSON string and you can use it as you want... for example by passing it to angular
If you need JSON object you should to something like this:
<script type="text/javascript">
var theJsonString = '${json}';
var theJsonObj = JSON.parse(theJsonString);
</script>
or
<script type="text/javascript">
var theJsonString = '<c:out value="${json}" />';
var theJsonObj = JSON.parse(theJsonString);
</script>
I hope this can help
Angelo
I was able to solve the problem but by redesigning the app and not by finding a real fix. What I was doing is starting Angular 2 app, have some navigation, functionality on the page, then one could click login, and Spring Security came into play. However, after verification I could not figure out how to launch Angular 2 app again: start a completely new one? Or somehow use the already launched to proceed with the development? Not sure.
What I did is just start the app by showing login page always at the very beginning and only after user logs in, Angular 2 comes into play. I guess, I could set up a second website to develop another Angular app, and make a link there that would launch this one, if we need some functionality before user logs in. The problem was that I was trying to split one Angular app into two, I guess.
Just in case, here is my security.xml part (Spring Security version 4.0.4.RELEASE):
<http use-expressions="true">
<intercept-url pattern="/views/**" access="hasAnyRole('ROLE_CUSTOMER', 'ROLE_ADMIN')" />
<form-login
login-page="/login.jsp"
login-processing-url="/views/home"
authentication-failure-url="/login.jsp?error=failed_login"
/>
<logout logout-url="/logout" logout-success-url="/login.jsp" delete-cookies="JSESSIONID" />
<remember-me />
<headers>
<cache-control/>
<xss-protection/>
</headers>
</http>
Login form:
<form class="form-signin" method="POST"
action="${pageContext.servletContext.contextPath}/views/home">
<h2 class="form-signin-heading">Please sign in</h2>
<label for="inputEmail" class="sr-only">Email address</label>
<input type="text" name="username" class="form-control" placeholder="Login" required autofocus>
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" name="password" class="form-control" placeholder="Password" required>
<div class="checkbox">
<label>
<input type="checkbox" value="remember-me"> Remember me
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
And a controller that launches Angular application upon successful log in:
#RequestMapping("/views/home")
public String login() {
return "index";
}
Then, in launched Angular 2 app I am planning to fetch the data by making HTTP requests, and using Springs' controllers to return me JSON. So, after Angular is launched it works by itself just interacting with the server via HTTP. After logging out a user will not get access to Angular app because it is under a protected folder and index.jsp is not in the app root folder, so it can not be launched by accessing / path.

Codeigniter Post not accepting string URL

I try to send an ajax post with URLs but it returns 403 forbidden, it works on my localhost but it does not work when deployed online (LAMP)
Form Code
<form id="Links">
<input type="url" name="link1" id="link1" />
<input type="url" name="link2" id="link2" />
<input type="button" value="submit" id="submitLink" />
</form>
Jquery Code
$(document).ready(function()({
$("#submitLink").click(function(){
$.post("http://mysite.com/mycontroller/myfunction", $("#Links").serialized(), function(data){
alert("success!");
});
});
});
PHP CI Function
public function myfunction()
{
print_r($this->input->post());
die();
}
Viewing the ajax post on firebug console.. it shows 403 forbidden.. in online deployment.. but it works on localhost.
P.S.
My global xss filtering in config is set to false
Their are following things which need to be consider.
Check the controller and method is present
Is any restriction applied through .htaccess
Check file permissions for reading and writing.

Spring Social prepoplating local registeration form with values from provider

I'm trying to pre-populate my local registration with Linkedin Callback. I am getting a NullPointer Exception on the "Email" field when I attempt to register.
It is possible that Email Address Field is not accessible with Linkedin but the Class UserProfile (used to populate fields) Docs say otherwise - "model provides access to those common fields in an uniform way".
It works with Facebook. When the Authentication process is triggered with a click on "Sign in with Facebook" Button, I am redirected back to the local site (after authentication) with the local signup form pre-populated with name and email.
This does not work for LinkedIn or Twitter, both throw NullPointer Exception. I would like settings for both LinkedIn and Twitter.
Here is my form
Linkedin
<form name="linkedin_signin" id="linkedin_signin" action="${linkedin_uri}" method="POST">
<input type="image" src="${linkedin_img}" />
</form>
Twitter
<form:form id="twitter_signin" action="${twitter_uri}" method="POST">
<input type="image" src="${twitter_img}" />
</form:form>
You must request r_emailaddress permission when authorizing your application for LinkedIn.
<form name="linkedin_signin" id="linkedin_signin" action="${linkedin_uri}" method="POST">
<input type="image" src="${linkedin_img}" />
<input type="hidden" name="scope" value="r_emailaddress" />
</form>
Permissions for LinkedIn: https://developer.linkedin.com/documents/authentication#granting
Permissions for Facebook: http://developers.facebook.com/docs/reference/login/#permissions
It looks like you can't do it with twitter https://dev.twitter.com/discussions/4019

Resources