Spring Security - Persistent Remember Me Issue - java

I've been trying to track down why Spring Security isn't creating the Spring Security remember me cookie (SPRING_SECURITY_REMEMBER_ME_COOKIE). However, based on what I see via the HTTP headers the cookie is being set it's just that there is an additional GET request for /j_spring_security_check that is causing the exception below. This also results in the cookie being removed.
FINE: Authentication request failed: org.springframework.security.authentication.AuthenticationServiceException: Authentication method not supported: GET
I'm using Spring 3.0.1, Spring Security 3.0.3 Snapshot, and Google App Engine 1.3.1. Any ideas as to what is going on?
Mar 17, 2010 10:38:35 AM org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter doFilter
FINE: Request is to process authentication
Mar 17, 2010 10:38:35 AM org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter unsuccessfulAuthentication
FINE: Authentication request failed: org.springframework.security.authentication.AuthenticationServiceException: Authentication method not supported: GET
Mar 17, 2010 10:38:35 AM org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter unsuccessfulAuthentication
FINE: Updated SecurityContextHolder to contain null Authentication
Mar 17, 2010 10:38:35 AM org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter unsuccessfulAuthentication
FINE: Delegating to authentication failure handlerorg.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler#4196c169
Mar 17, 2010 10:38:35 AM org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices loginFail
FINE: Interactive login attempt was unsuccessful.
Mar 17, 2010 10:38:35 AM org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices cancelCookie
FINE: Cancelling cookie
Below is the relevant portion of the applicationContext-security.xml.
<http auto-config="false">
<intercept-url pattern="/css/**" filters="none" />
<intercept-url pattern="/img/**" filters="none" />
<intercept-url pattern="/js/**" filters="none" />
<intercept-url pattern="/app/admin/**" filters="none" />
<intercept-url pattern="/app/login/**" filters="none" />
<intercept-url pattern="/app/register/**" filters="none" />
<intercept-url pattern="/app/error/**" filters="none" />
<intercept-url pattern="/" filters="none" />
<intercept-url pattern="/**" access="ROLE_USER" />
<logout logout-success-url="/" />
<form-login login-page="/app/login" default-target-url="/" authentication-failure-url="/app/login?login_error=1" />
<session-management invalid-session-url="/app/login" />
<remember-me services-ref="rememberMeServices" key="myKey" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userDetailsService">
<password-encoder hash="sha-256" base64="true">
<salt-source user-property="username" />
</password-encoder>
</authentication-provider>
</authentication-manager>
<beans:bean id="userDetailsService" class="com.my.service.auth.UserDetailsServiceImpl" />
<beans:bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
<beans:property name="userDetailsService" ref="userDetailsService" />
<beans:property name="tokenRepository" ref="persistentTokenRepository" />
<beans:property name="key" value="myKey" />
</beans:bean>
<beans:bean id="persistentTokenRepository" class="com.my.service.auth.PersistentTokenRepositoryImpl" />
Below are the http headers for the scenario I'm having issues with:
http://localhost:8080/j_spring_security_check
POST /j_spring_security_check HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://localhost:8080/app/login
Cookie: JSESSIONID=15t2gq1vo5noj
Content-Type: application/x-www-form-urlencoded
Content-Length: 88
j_username=test%40test.com&j_password=test&_spring _security_remember_me=on&submit=Submit
HTTP/1.1 302 Found
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Set-Cookie: JSESSIONID=1dymxpkh13z32;Path=/
Set-Cookie: SPRING_SECURITY_REMEMBER_ME_COOKIE=U05kS2NTakNIZTN Dd0hFcWxqZXRUQT09Oi90M3Q0NTA1czhxSjRadTQ5NW5FQVE9P Q;Path=/;Expires=Wed, 31-Mar-10 10:52:07 GMT
Location: http://localhost:8080/app/helloWorld
Content-Length: 0
Server: Jetty(6.1.x)
----------------------------------------------------------
http://localhost:8080/app/helloWorld
GET /app/helloWorld HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://localhost:8080/app/login
Cookie: JSESSIONID=1dymxpkh13z32; SPRING_SECURITY_REMEMBER_ME_COOKIE=U05kS2NTakNIZTN Dd0hFcWxqZXRUQT09Oi90M3Q0NTA1czhxSjRadTQ5NW5FQVE9P Q
HTTP/1.1 200 OK
Content-Language: en-US
Content-Type: text/html
Content-Length: 526
Server: Jetty(6.1.x)
----------------------------------------------------------
http://localhost:8080/j_spring_security_check
GET /j_spring_security_check HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Cookie: JSESSIONID=1dymxpkh13z32; SPRING_SECURITY_REMEMBER_ME_COOKIE=U05kS2NTakNIZTN Dd0hFcWxqZXRUQT09Oi90M3Q0NTA1czhxSjRadTQ5NW5FQVE9P Q
HTTP/1.1 302 Found
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Set-Cookie: SPRING_SECURITY_REMEMBER_ME_COOKIE=;Path=/;Expires=Thu, 01 Jan 1970 00:00:00 GMT
Location: http://localhost:8080/app/login?login_error=1
Content-Length: 0
Server: Jetty(6.1.x)
----------------------------------------------------------
http://localhost:8080/app/login?login_error=1
GET /app/login?login_error=1 HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Cookie: JSESSIONID=1dymxpkh13z32
HTTP/1.1 200 OK
Content-Language: en-US
Content-Type: text/html
Content-Length: 928
Server: Jetty(6.1.x)

Turns out that Firebug was generating the additional GET request.

Have you tried using the simplest version of remember-me ?
<remember-me key="myAppKey"/>
Could be that your custom implementation of the token repository is at fault. What does your PersistentTokenRepositoryImpl actually do?
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/remember-me.html

Remember Me was broken in Spring Security 3.0.1: SEC-1356. Use 3.0.2.

Related

how to rejected request missing header or wrong header in wso2

I have code in API WSO2 like this
<api context="/request" name="Request" xmlns="http://ws.apache.org/ns/synapse">
<resource methods="GET" uri-template="/Executionreport/{vtid}/{token}">
<inSequence>
<property expression="get-property('uri.var.vtid')" name="Vtid" scope="default" type="STRING"/>
<property expression="get-property('uri.var.token')" name="token" scope="default" type="STRING"/>
<filter regex="1234" source="$ctx:token">
<then>
<log>
<property name="sukses masuk token" value="0"/>
</log>
<sequence key="select_Execution_report"/>
<property name="messageType" scope="axis2" type="STRING" value="application/json"/>
</then>
<else/>
</filter>
</inSequence>
<outSequence/>
<faultSequence/>
</resource>
</api>
so for this request I will use like this one
http://localhost:8280/request/Executionreport/1100/1234
so if the token is wrong I will replay this one
HTTP/1.1 200 OK
soapaction: urn:mediate
Host: localhost:8280
Accept-Encoding: gzip,deflate
cache-control:
Content-Type: application/json; charset=UTF-8
Date: Thu, 23 Jul 2020 07:51:59 GMT
Transfer-Encoding: chunked
Connection: Keep-Alive
{ "status":"401 Not Authenticated" }
i want to change the logic like the request is like
http://localhost:8280/request/Executionreport/1100 with methods GET and have header token=1234
so if token wrong in header request wso2 will rejected so status will be HTTP/1.1 401 unauthorized.
so my question how to create API methods GET and need header in WSO2 ?
You can read the incoming header as below.
<property name="Token" expression="get-property('token')" scope="transport" type="STRING" />
Based on that you can perform any action.

Tomcat randomly returns 401 for POST request with binary file

I am running a backbone.js app on frontend and Spring boot (2.13) app on Tomcat 9 as backend. We are using OpenJDK Java 11. (Corretto)
All my other requests(GET and POST) work. Only two POST requests that upload binary file sometimes return 401. We dont see anything in backend log for the unsucessful try, only Tomcat access log shows 401. The request fails immediately. Inspecting the requests in Chrome shows they looks the same - same number of headers and same values.
Sucessful request looks like this
[03/Dec/2019:13:49:49 +0100] 172.30.. - **user CN=*****, O=****, C=** "POST /rest/taxUpload HTTP/1.1" 200 61 "https://*********.eu/" "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
Unsucessful request looks like this - there are always two lines in the access log
[03/Dec/2019:13:50:13 +0100] 172.30.. - **user - "POST /rest/taxUpload HTTP/1.1" 401 1099 "https://*********.eu/" "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
[03/Dec/2019:17:13:40 +0100] 172.30.***.* - --- - "null null HTTP/1.1" 400 2297 "-" "-"
Requests were made for the same file, same browser, same user.
We use The DataSource Database Realm on Tomcat for authentication, we tried TLS1.1, TLS1.2,
Tomcat 8.5, different browser, all cache and timeout settings on SSL Connector in Tomcat.
Headers being sent
POST /rest/taxUpload HTTP/1.1
Host: *****.net
Connection: keep-alive
Content-Length: 160093
Accept: /
Origin: https://*****.net
X-CSRF-TOKEN: 344f5e99-b5e5-49c1-8711-27de638dd739
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
Content-Type: multipart/form-data; boundary=----
WebKitFormBoundaryo5NZhRls0pjEqx8Z
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Referer: https://******.net/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: _ga=GA1.2.1549123445.1568710333; CSRF-TOKEN=344f5e99-b5e5-49c1-8711-27de638dd739
Tomcat server.xml
<Connector port="443"
protocol="org.apache.coyote.http11.Http11NioProtocol"
SSLEnabled="true"
maxThreads="250"
scheme="https"
xpoweredBy="false"
server="****"
disableUploadTimeout="true"
maxSavePostSize="4096"
clientAuth="false"
keyAlias="default"
keyPass="***"
keystoreFile="***/default.jks"
keystorePass="****"
keystoreType="JKS"
sslProtocol="TLS"
ciphers="TLS_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,SSL_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"
sslEnabledProtocols="TLSv1.2+TLSv1.1+TLSv1"
sslImplementationName="org.apache.tomcat.util.net.jsse.JSSEImplementation"
truststoreFile="***.jks"
truststorePass="****"
truststoreType="JKS"
secure="true"
relaxedQueryChars="[,]"
acceptCount="250"
acceptorThreadCount="2"
compression="on"/>
<Realm className="org.apache.catalina.realm.DataSourceRealm"
dataSourceName="jdbc/authority"
userTable="users" userNameCol="user_name" userCredCol="user_pass"
userRoleTable="user_roles" roleNameCol="role_name" localDataSource="true"/>

Overriding container response for spring-security BadCredentialsException

I have a trivial REST app that needs a few resources secured with HTTP Basic auth. I don't ever want to see container generated HTML in response to anything that a client of this app might do. So I have setup my SpringSecurity like this:
<http pattern="/api/**" auto-config="true" create-session="stateless" entry-point-ref="entryPoint">
<intercept-url pattern="/**" method="PUT" access="ROLE_ADMIN" />
<intercept-url pattern="/**" method="POST" access="ROLE_ADMIN" />
<intercept-url pattern="/**" method="DELETE" access="ROLE_ADMIN" />
<http-basic />
</http>
<http security="none" />
<authentication-manager>
<authentication-provider>
<user-service id="userDetailsService"
properties="classpath:META-INF/spring/users.properties"/>
</authentication-provider>
</authentication-manager>
My EntryPoint class looks like this:
public class BasicJsonEntryPoint extends BasicAuthenticationEntryPoint {
#Override
public void commence(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException
) throws IOException, ServletException {
response.addHeader("WWW-Authenticate", "Basic realm=\"" + getRealmName() + "\"");
response.setStatus(SC_UNAUTHORIZED);
PrintWriter writer = response.getWriter();
writer.println("{\"status\":" + SC_UNAUTHORIZED + ", \"message\":\"" + authException.getMessage() + "\"}");
}
}
This works when I hit a protected resource/method combination and I get something like the following;
HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
WWW-Authenticate: Basic realm="Admin"
Content-Length: 84
Date: Sun, 29 Sep 2013 11:50:48 GMT
{"status":401, "message":"Full authentication is required to access this resource"}
.. which is exactly what I want. However, if I supply invalid credentials in the Authorize: HTTP header, I get Tomcat's HTML response...
HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
WWW-Authenticate: Basic realm="Admin"
Content-Type: text/html;charset=utf-8
Content-Length: 1019
Date: Sun, 29 Sep 2013 11:51:14 GMT
<html><head><title>Apache Tomcat/7.0.33 - Error report</title><style><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 401 - Invalid basic authentication token</h1><HR size="1" noshade="noshade"><p><b>type</b> Status report</p><p><b>message</b> <u>Invalid basic authentication token</u></p><p><b>description</b> <u>This request requires HTTP authentication.</u></p><HR size="1" noshade="noshade"><h3>Apache Tomcat/7.0.33</h3></body></html>
What else do I have to override/implement in order to get the same JSON response here (ideally using the same class I already created)?
Many thanks,
The problem is in the BasicAuthenticationFilter, which is using it's own instance of BasicAuthenticationEntryPoint instead of your implementation. So you can tweak it a bit:
<http auto-config="true" create-session="stateless">
<intercept-url .../>
<http-basic />
<custom-filter ref="basicAuthenticationFilter" before="BASIC_AUTH_FILTER" />
</http>
<beans:bean id="basicAuthenticationFilter"
class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="authenticationEntryPoint" ref="entryPoint" />
</beans:bean>
<beans:bean id="entryPoint" class="mypackage.BasicJsonEntryPoint">
<beans:property name="realmName" value="realm"/>
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider>
...
</authentication-provider>
</authentication-manager>

Spring Security, REST basic authentication issue

I got an issue related to the HTTP response header "Access-Control-Allow-Origin" when using basic authetication with Spring. When I authenticate manually, like the code bellow (I'm using REST):
#RequestMapping(value = "/login", method = RequestMethod.POST, consumes = "application/json")
#ResponseStatus(value = HttpStatus.OK)
public void login(#RequestBody String body, HttpServletResponse response)
throws IOException {
try {
User user = gson.fromJson(body, User.class);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
usuario.getUsername(), usuario.getPassword());
authenticationManager.authenticate(token);
} catch (BadCredentialsException e) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
} catch (Exception e) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
everything works fine, I receive the following HTTP response:
HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
Content-Type: text/html;charset=utf-8
Content-Length: 951
Date: Fri, 17 May 2013 19:14:36 GMT
as you can see, "Access-Control-Allow-Origin" is present on the response.
Everything is fine here. I can catch a 401 error in my ajax call.
But when the authentication is performed automatically, like the code bellow:
#RequestMapping(value = "/name", method = RequestMethod.POST, consumes = "application/json")
#PreAuthorize("hasRole('ROLE_CUSTOMER')")
public #ResponseBody String getName(HttpServletResponse response) throws IOException {
String json = null;
try {
User userSession = (User) SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
Customer customer = customerDao.getNameByUsername(userSession.getUsername());
json = gson.toJson(customer);
} catch (Exception e) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
return json;
}
the HTTP response is:
HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
WWW-Authenticate: Basic realm="Spring Security Application"
Content-Type: text/html;charset=utf-8
Content-Length: 981
Date: Fri, 17 May 2013 19:41:08 GMT
There is no "Access-Control-Allow-Origin" in the response
Google Chrome console show the following error:
Origin null is not allowed by Access-Control-Allow-Origin
My ajax call does not return a 401 Unauthorized error, even though the HTTP response return it (response above), I receive an unknow error.
I figured out that for all browsers, I need a "Access-Control-Allow-Origin" in the HTTP response, otherwise they will generate some kind of silent error and my ajax call will fail (can't catch the 401 error). Actually, javascript will fail silently. XMLHttpRequest
does not accept an HTTP response without "Access-Control-Allow-Origin".
How can I make Spring inject this "Access-Control-Allow-Origin" in HTTP responses for basic authentication?
this is my Spring Security xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="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.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<security:http create-session="stateless" entry-point-ref="authenticationEntryPoint">
<security:intercept-url pattern="/customer/**" />
<security:http-basic />
<security:custom-filter ref="basicAuthenticationFilter"
after="BASIC_AUTH_FILTER" />
</security:http>
<bean id="basicAuthenticationFilter"
class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
<property name="authenticationEntryPoint" ref="authenticationEntryPoint" />
</bean>
<bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
<property name="realmName" value="teste.com" />
</bean>
<!-- It is responsible for validating the user's credentials -->
<security:authentication-manager alias="authenticationManager">
<!-- It is responsible for providing credential validation to the AuthenticationManager -->
<security:authentication-provider>
<security:password-encoder ref="passwordEncoder" />
<security:jdbc-user-service
data-source-ref="mySQLdataSource"
users-by-username-query="select username, password, enabled from usuario where username = ?"
authorities-by-username-query="select username, papel from autoridade where username = ?" />
</security:authentication-provider>
</security:authentication-manager>
<bean class="org.springframework.security.crypto.password.StandardPasswordEncoder"
id="passwordEncoder" />
</beans>
Just found my own way:
First of all, I don't really remember why I put this line here, but it was messing up my code:
<security:http-basic />
Second, this answer show me the path: Handle unauthorized error message for Basic Authentication in Spring Security. I had to create a custom authentication entry point in order to send the Access-Control-Allow-Origin thing.
So this is my code now:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="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.2.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<security:http create-session="stateless"
entry-point-ref="authenticationEntryPoint">
<security:intercept-url pattern="/api/admin/**" />
<security:intercept-url pattern="/medico/**" />
<!-- <security:http-basic /> -->
<security:custom-filter ref="basicAuthenticationFilter"
after="BASIC_AUTH_FILTER" />
</security:http>
<bean id="basicAuthenticationFilter"
class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
<property name="authenticationEntryPoint" ref="authenticationEntryPoint" />
</bean>
<!--
<bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
<property name="realmName" value="test.com" />
</bean> -->
<bean id="authenticationEntryPoint"
class="com.test.util.PlainTextBasicAuthenticationEntryPoint">
<property name="realmName" value="test.com" />
</bean>
<!-- It is responsible for validating the user's credentials -->
<security:authentication-manager alias="authenticationManager">
<!-- It is responsible for providing credential validation to the AuthenticationManager -->
<security:authentication-provider>
<security:password-encoder ref="passwordEncoder" />
<security:jdbc-user-service
data-source-ref="mySQLdataSource"
users-by-username-query="select username, password, enabled from usuario where username = ?"
authorities-by-username-query="select username, papel from autoridade where username = ?" />
</security:authentication-provider>
</security:authentication-manager>
<bean
class="org.springframework.security.crypto.password.StandardPasswordEncoder"
id="passwordEncoder" />
</beans>
package com.test.util;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
public class PlainTextBasicAuthenticationEntryPoint extends
BasicAuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.addHeader("Access-Control-Allow-Origin", "null");
response.addHeader("WWW-Authenticate", "Basic realm=\"" + getRealmName() + "\"");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter writer = response.getWriter();
writer.println("HTTP Status " + HttpServletResponse.SC_UNAUTHORIZED + " - " + authException.getMessage());
}
}
My http response now:
HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: null
WWW-Authenticate: Basic realm="test.com"
Content-Length: 35
Date: Mon, 20 May 2013 20:05:03 GMT
HTTP Status 401 - Bad credentials
before the alteration, I got this error message:
OPTIONS http://localhost:8080/test/customer/name 200 (OK) jquery-1.8.2.min.js:2
XMLHttpRequest cannot load http://localhost:8080/test/customer/name. Origin null is not allowed by Access-Control-Allow-Origin.
and now as expected I get this one:
OPTIONS http://localhost:8080/test/customer/name 200 (OK) jquery-1.8.2.min.js:2
POST http://localhost:8080/test/customer/name 401 (Unauthorized)

spring-ws: getting an EchoEndpoint to work, not return 404

The short version: My Spring-WS SOAP EchoEndpoint responds 404 whenever I try to access it. Please help, I've spent two days trying to get it to work correctly. :-I
Longer version: I've got a Spring project where I need to make SOAP services that act, look and feel as SOAP services from the system that will be deprecated. Therefore I'm not going to be using marshalling, but rather analyse the envelope contents via XPath and make the response in a way that looks like the old system.
But leading up to that I'm having problems. I have wired spring-ws up through DispatcherServlet instead of MessageDispatcherServlet because it lives in the same space as the rest of my WEB applications REST services. Here are my related beans:
<context:component-scan base-package="com.saers.niklas.view"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="" p:suffix=".jsp"/>
<bean name="jsonView" class="org.springframework.web.servlet.view.json.JsonView">
<property name="encoding" value="UTF-8"/>
<property name="contentType" value="application/json"/>
<property name="hijackSafe" value="true"/>
<property name="hijackSafePrefixPostFix"><value>(while(1);)</value></property>
</bean>
<aop:aspectj-autoproxy />
<sws:annotation-driven />
<context:component-scan base-package="com.saers.niklas.ws"/>
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<bean class="com.saers.niklas.view.web.interceptors.AddObjectIDsToResponseInterceptor"/>
</property>
</bean>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="defaultHandler" ref="messageDispatcher"/>
</bean>
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher"/>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<bean class="org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter">
<property name="messageFactory">
<bean class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory" />
</property>
</bean>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
My endpoint looks like this
package com.saers.niklas.ws;
import javax.xml.transform.Source;
import org.w3c.dom.*;
import org.springframework.ws.server.endpoint.annotation.*;
import org.springframework.xml.transform.StringSource;
#Endpoint
public class EchoEndpoint {
public EchoEndpoint() {
System.err.println("XXNJSXX: EchoEndPoint");
}
#PayloadRoot(localPart="echoRequest", namespace="http://niklas.saers.com/DemoService")
public Source echo(#RequestPayload Element requestElement) {
return new StringSource("<x/>");
}
}
and my test looks like this:
package com.saers.niklas.ws;
import javax.xml.transform.*;
import junit.framework.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.xml.transform.*;
public class WSTest {
private static WebServiceTemplate template;
#BeforeClass
public static void setUp() {
template = new WebServiceTemplate();
template.setDefaultUri("http://localhost:8080/MyApp/ws/");
}
#Test
public void testEchoEndpoint() {
String xml = "<echoRequest xmlns=\"http://niklas.saers.com/DemoService\">Hello world</echoRequest>";
Source source = new StringSource(xml);
Result result = new StringResult();
try {
template.sendSourceAndReceiveToResult(source, result);
System.out.println(result);
} catch (Exception ex) {
ex.printStackTrace(System.err);
Assert.fail();
}
}
}
The test fails with
org.springframework.ws.client.WebServiceTransportException: Not Found [404]
at org.springframework.ws.client.core.WebServiceTemplate.handleError(WebServiceTemplate.java:622)
at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:546)
at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:496)
at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:451)
at org.springframework.ws.client.core.WebServiceTemplate.sendSourceAndReceiveToResult(WebServiceTemplate.java:395)
at org.springframework.ws.client.core.WebServiceTemplate.sendSourceAndReceiveToResult(WebServiceTemplate.java:386)
at org.springframework.ws.client.core.WebServiceTemplate.sendSourceAndReceiveToResult(WebServiceTemplate.java:376)
at com.saers.niklas.ws.WSTest.testKommuneEndpoint(WSTest.java:65)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
even though I get "XXNJSXX: EchoEndPoint" in the output when I start the application, so I know that it is initialized, probably just not hooked up correctly. Whenever I run my test I also see a line like:
2011-06-16 09:57:10 SaajSoapMessageFactory [INFO] Creating SAAJ 1.3 MessageFactory with SOAP 1.1 Protocol
I've also run WireShark to confirm that the request is going through exactly the way I expect it, and I really get a 404 back. Here is the request:
POST /MyApp/ws/ HTTP/1.1
Accept-Encoding: gzip
Accept: text/xml, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
SOAPAction: ""
Content-Type: text/xml; charset=utf-8
Cache-Control: no-cache
Pragma: no-cache
User-Agent: Java/1.6.0_26
Host: localhost:8080
Connection: keep-alive
Content-Length: 229
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><SOAP-ENV:Body><echoRequest xmlns="http://niklas.saers.com/DemoService">Hello world</echoRequest></SOAP-ENV:Body></SOAP-ENV:Envelope>
What am I missing?
EDIT1: I was asked if I had the same problem with MessageDispatcherServlet, so I've set one up and yes, it seems I do. Here is the addition to web.xml:
<servlet>
<servlet-name>MDS</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:ws-servlet.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MDS</servlet-name>
<url-pattern>/soap2/*</url-pattern>
</servlet-mapping>
All that's in ws-servlet.xml is:
<context:annotation-config />
<context:component-scan base-package="com.saers.niklas.ws"/>
<sws:annotation-driven />
I get the same 404 when calling the EchoRequest:
POST /MyApp/soap2/ HTTP/1.1
Accept-Encoding: gzip
Accept: text/xml, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
SOAPAction: ""
Content-Type: text/xml; charset=utf-8
Cache-Control: no-cache
Pragma: no-cache
User-Agent: Java/1.6.0_26
Host: localhost:8080
Connection: keep-alive
Content-Length: 229
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><SOAP-ENV:Body><echoRequest xmlns="http://niklas.saers.com/DemoService">Hello world</echoRequest></SOAP-ENV:Body></SOAP-ENV:Envelope>
But this time I'm not sure if that's because my web.xml has configured with DispatcherServlet to / further down in the file:
<servlet-mapping>
<servlet-name>MyApp</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Cheers
Nik
Can you try using a normal MessageDispatcherServlet instead (as recommended here http://forum.springsource.org/showthread.php?39673-Spring-WS-in-a-normal-spring-dispatcher-servlet though that's an old thread)? That's worked for me when I need both HTML and WS responses from the same webapp. It's simple to share beans between the two dispatchers if you pull out the common beans into a separate XML config file. Plus you appear to be using classpath scanning which makes it even easier. You can follow the instructions here http://springtips.blogspot.com/2007/06/using-shared-parent-application-context.html to share the singelton instances as well as the configuration of your beans.
PS There's no need for
} catch (Exception ex) {
ex.printStackTrace(System.err);
Assert.fail();
}
in test code, just make the test method throw Exception. You'll still know the test needs fixing, and in Eclipse/Junit you'll get a nicer stack trace.
In my case, the solution was to pay attention to the case in the URI. I had it in all lower case, but the webservice was expecting a CamelCase action name.

Categories

Resources