I try to configure CORS policy, but it doesn't work.
When i send a request i recieve headers like this:
* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 0
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Mon, 13 Feb 2023 10:33:36 GMT
with no allow-origins. How can i fix this problem?
Spring security config code:
#Configuration
#EnableWebSecurity
#EnableMethodSecurity
public class JWTSecurityConfig {
//here is jwt setting
...
#Bean
public SecurityFilterChain filterChain(HttpSecurity http, Jwt2AuthenticationConverter authenticationConverter, ServerProperties serverProperties) throws Exception {
http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(authenticationConverter);
http.anonymous();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.csrf().disable().cors().disable();
http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\"");
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
});
if (serverProperties.getSsl() != null && serverProperties.getSsl().isEnabled()) {
http.requiresChannel().anyRequest().requiresSecure();
} else {
http.requiresChannel().anyRequest().requiresInsecure();
}
http.authorizeRequests().requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.and().authorizeRequests().requestMatchers("/swagger-ui/**", "/api-docs/**").permitAll()
.and().authorizeRequests().requestMatchers(HttpMethod.GET, "/attributes/questions", "/attr-values/recent-values", "/attr-values/history").hasAuthority("default-roles-ai-solutions").requestMatchers(HttpMethod.POST, "/attr-values/save-attr-values").hasAuthority("default-roles-ai-solutions").anyRequest().authenticated().and().oauth2ResourceServer().jwt();
return http.build();
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final var configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setExposedHeaders(Arrays.asList("*"));
final var source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
dependencies in pom.xml
spring-boot-starter-web:3.0.0
spring-boot-starter-data-jpa: 3.0.0
spring-boot-starter-oauth2-resource-server:3.0.0
When i enable cors like this:
http.cors().configurationSource(corsConfigurationSource());
i recieve if request isn't correct:
* Mark bundle as not supporting multiuse
< HTTP/1.1 403
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 0
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Transfer-Encoding: chunked
< Date: Mon, 13 Feb 2023 12:46:25 GMT
if it's correct request:
Mark bundle as not supporting multiuse
< HTTP/1.1 200
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Allow: GET,HEAD,OPTIONS
< Accept-Patch:
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 0
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Length: 0
< Date: Mon, 13 Feb 2023 13:28:44 GMT
part with jwt config
public interface Jwt2AuthoritiesConverter extends Converter<Jwt, Collection<? extends GrantedAuthority>> {
}
#SuppressWarnings("unchecked")
#Bean
public Jwt2AuthoritiesConverter authoritiesConverter() {
// This is a converter for roles as embedded in the JWT by a Keycloak server
// Roles are taken from both realm_access.roles & resource_access.{client}.roles
return jwt -> {
final var realmAccess = (Map<String, Object>) jwt.getClaims().getOrDefault("realm_access", Map.of());
final var realmRoles = (Collection<String>) realmAccess.getOrDefault("roles", List.of());
final var resourceAccess = (Map<String, Object>) jwt.getClaims().getOrDefault("resource_access", Map.of());
// We assume here you have "employee-service" (as in the tutorial referenced in the question) and "other-client" clients configured with "client roles" mapper in Keycloak
final var confidentialClientAccess = (Map<String, Object>) resourceAccess.getOrDefault("employee-service", Map.of());
final var confidentialClientRoles = (Collection<String>) confidentialClientAccess.getOrDefault("roles", List.of());
final var publicClientAccess = (Map<String, Object>) resourceAccess.getOrDefault("other-client", Map.of());
final var publicClientRoles = (Collection<String>) publicClientAccess.getOrDefault("roles", List.of());
final var realmScopes = Arrays.asList(((String) jwt.getClaims().getOrDefault("scope", "")).split(" "));
realmScopes.replaceAll(scope -> "SCOPE_" + scope);
final var grantedAuthorityScopes = (Collection<String>) realmScopes;
return Stream.concat(realmRoles.stream(), Stream.concat(confidentialClientRoles.stream(), Stream.concat(publicClientRoles.stream(), grantedAuthorityScopes.stream()))).map(SimpleGrantedAuthority::new).toList();
};
}
public interface Jwt2AuthenticationConverter extends Converter<Jwt, AbstractAuthenticationToken> {
}
#Bean
public Jwt2AuthenticationConverter authenticationConverter(Jwt2AuthoritiesConverter authoritiesConverter) {
return jwt -> new JwtAuthenticationToken(jwt, authoritiesConverter.convert(jwt));
}
I expect to recieve:
Access-Control-Allow-Origin: *
I think the error is at line: http.csrf().disable().cors().disable();. You should use istead http.csrf().disable().cors().configurationSource(corsConfigurationSource()); and the method corsConfigurationSource is not a #Bean.
#Configuration
#EnableWebSecurity
#EnableMethodSecurity
public class JWTSecurityConfig {
...
#Bean
public SecurityFilterChain filterChain(HttpSecurity http, Jwt2AuthenticationConverter authenticationConverter, ServerProperties serverProperties) throws Exception {
http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(authenticationConverter);
http.anonymous();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.csrf().disable().cors().configurationSource(corsConfigurationSource());
http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\"");
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
});
if (serverProperties.getSsl() != null && serverProperties.getSsl().isEnabled()) {
http.requiresChannel().anyRequest().requiresSecure();
} else {
http.requiresChannel().anyRequest().requiresInsecure();
}
http.authorizeRequests().requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.and().authorizeRequests().requestMatchers("/swagger-ui/**", "/api-docs/**").permitAll()
.and().authorizeRequests().requestMatchers(HttpMethod.GET, "/attributes/questions", "/attr-values/recent-values", "/attr-values/history").hasAuthority("default-roles-ai-solutions").requestMatchers(HttpMethod.POST, "/attr-values/save-attr-values").hasAuthority("default-roles-ai-solutions").anyRequest().authenticated().and().oauth2ResourceServer().jwt();
return http.build();
}
CorsConfigurationSource corsConfigurationSource() {
final var configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setExposedHeaders(Arrays.asList("*"));
final var source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
EDITED
You should replace configuration.setAllowedOrigins(Arrays.asList("*")) with configuration.addAllowedOriginPattern("*").
CorsConfigurationSource corsConfigurationSource() {
final var configuration = new CorsConfiguration();
configuration.addAllowedOriginPattern("*");
configuration.setAllowedMethods(Arrays.asList("GET"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setExposedHeaders(Arrays.asList("*"));
final var source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
Both #david-pérez-cabrera and #m-denium are right
Replace cors().disable() with configurationSource(corsConfigurationSource()).
Your authentication converter is not picked
Spring-boot expects a Converter<Jwt, ? extends AbstractAuthenticationToken> bean when you provide a Converter<Jwt, AbstractAuthenticationToken>, and guess what?, AbstractAuthenticationToken does not extend AbstractAuthenticationToken...
The 403 you get with http.cors().configurationSource(corsConfigurationSource()); is probably not a CORS error.
Given your conf, my guess (we don't have the details of the requests you attempt) is that you are missing the default-roles-ai-solutions authority when issuing either:
a GET request to one of "/attributes/questions", "/attr-values/recent-values", "/attr-values/history"
a POST request to "/attr-values/save-attr-values"
Two options:
correctly copy from my code, including this part: oauth2ResourceServer().jwt().jwtAuthenticationConverter(...)
change your Jwt2AuthenticationConverter interface definition from extends Converter<Jwt, AbstractAuthenticationToken> to extends Converter<Jwt, JwtAuthenticationToken>
Related
public class ApiResponse {
long timestamp;
int status;
String error;
String message;
String path;
public ApiResponse(HttpStatus status, String message) {
super();
this.status = status.value();
this.message = message;
}
}
I have this class and what i want to do is set response status based on status value of ApiResponse object
return new ApiResponse(HttpStatus.CREATED, "Success");
I want response status to be 201.
Is something like this possible?
I know there is ResponseEntity class but i want to handle it without using this class.
Use the ResponseBodyAdvice interface to process the response uniformly.
#RestControllerAdvice
public class TransformStatusBodyAdvice implements ResponseBodyAdvice<ApiResponse> {
#Override
public boolean supports(MethodParameter returnType, Class converterType) {
// check return type is ApiResponse
return returnType.getParameterType().isAssignableFrom(ApiResponse.class);
}
#Override
public ApiResponse beforeBodyWrite(ApiResponse body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body != null) {
// change something if you want
// set the response status code
response.setStatusCode(HttpStatus.valueOf(body.getStatus()));
}
return body;
}
}
Test Controller
#RestController
#RequestMapping("/test")
public class TestController {
#PostMapping
public ApiResponse create() {
return new ApiResponse(HttpStatus.CREATED, "Success");
}
}
Use curl to test result. Now the http response code is 201. If you change your HttpStatus in the ApiResponse, the http status response code will be change.
$ curl -v -X POST http://localhost:8080/test
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> POST /test HTTP/1.1
> ...
>
< HTTP/1.1 201
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Mon, 05 Jul 2021 03:45:00 GMT
<
{"timestamp":0,"status":201,"error":null,"message":"Success","path":null}* Connection #0 to host localhost left intact
I am using Spring's org.springframework.boot.test.web.client.TestRestTemplate for testing controller code.
I can test the GET APIs simply by using testRestTemplate.withBasicAuth("test", "test").exchange(...), but the same way isn't working for the POST endpoints in same controller.
It returns HttpStatus 302 found with the following ResponseEntity:
<302,[Set-Cookie:"JSESSIONID=332C559B7CABE5682EE9910A6FF834DA; Path=/; HttpOnly", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY", Location:"http://localhost:61598/login", Content-Length:"0", Date:"Thu, 09 Jul 2020 14:24:18 GMT", Server:"Application Server"]>
Code of controller:
#GetMapping(value = "/filterable_columns", produces = "application/json")
public List<FilterableField> filterableFieldList() {
log.info("Received request for list of filterable fields");
return metaDataService.filterableFieldList(referenceDataService.getSchemaUri());
}
#PostMapping(value = "/search", produces = "application/json")
public List<Map<String, Object>> filteredSearch(#RequestBody FilteredSearchRequest filteredSearchRequest) throws IllegalAccessException {
log.info("Received request for filtered search");
return referenceDataService.filteredSearch(filteredSearchRequest);
}
Controller Test:
#Test // This works as expected
void filterableFieldList() {
val reply = testRestTemplate.withBasicAuth("test", "test")
.exchange("/reference_data/filterable_columns",
HttpMethod.GET, null,
new ParameterizedTypeReference<List<FilterableField>>() {
});
assertEquals(HttpStatus.OK, reply.getStatusCode());
assertFalse(Objects.requireNonNull(reply.getBody()).isEmpty());
}
#Test // This does not work
void filteredSearch() {
val reply = testRestTemplate.withBasicAuth("test", "test")
.exchange("/reference_data/search",
HttpMethod.POST,
new HttpEntity<>(new FilteredSearchRequest()),
new ParameterizedTypeReference<List<Map<String, Object>>>() {
}
);
System.out.println(reply);
assertEquals(HttpStatus.OK, reply.getStatusCode());
}
AdfsSecurityConfiguration.java:
#Configuration
#ConditionalOnProperty(prefix = "moneta.security.adfs", name = "enabled", matchIfMissing = true)
public class AdfsSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final Environment environment;
private final AdfsConfigurer<HttpSecurity> adfsConfigurer;
public AdfsSecurityConfiguration(final Environment environment, final AdfsConfigurer<HttpSecurity> adfsConfigurer) {
this.environment = environment;
this.adfsConfigurer = adfsConfigurer;
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
if (!isRunningLocally()) {
http.requiresChannel().anyRequest().requiresSecure();
}
http.apply(adfsConfigurer).and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests()
.requestMatchers(EndpointRequest.to("keepalive", "info", "health", "env"), EndpointRequest.toLinks())
.permitAll().anyRequest().authenticated().and().csrf().disable();
}
private boolean isRunningLocally() {
return environment.acceptsProfiles(Profiles.of("default"));
}
}
application-test.yml:
spring:
security:
user:
name: test
password: test
Trying to add Headers Authorization on restTemplate.exchange, the body returns null. but the request is valid and the body always contains something.
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", this.getToken("public","public") );
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<String> response = restTemplate.exchange(uriFinal, HttpMethod.GET, request, String.class);
log.debug( response.getBody() );
profiling with debug, I got the results:
===========================request begin================================================
URI : http://localhost:8080/api/department?pageNumber=0&pageSize=20
Method : GET
Headers : [Accept:"text/plain, application/json, application/*+json, */*", Content-Type:"application/json", Authorization:"Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJwdWJsaWMiLCJleHAiOjE1Njk1NjQ5MTl9.o0N-y6dxXDPjvviRA66AR1gppwN31XEAjT3mttSdhTbUCuBlBFTdSIjnPyLqPyvyEu1sKIBax1QoKglejJ0cgw", Content-Length:"0"]
Request body 1:
==========================request end================================================
============================response begin==========================================
Status code : 200 OK
Status text :
Headers : [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", Set-Cookie:"JSESSIONID=B0F5DFA38D26AD57C489F1366D291780; Path=/; HttpOnly", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", Content-Type:"application/json", Transfer-Encoding:"chunked", Date:"Thu, 26 Sep 2019 14:03:07 GMT"]
Response body 2: {"content":[],"pageable":{"sort":{"sorted":false,"unsorted":true,"empty":true},"offset":0,"pageSize":20,"pageNumber":0,"paged":true,"unpaged":false},"totalPages":0,"totalElements":0,"last":true,"number":0,"sort":{"sorted":false,"unsorted":true,"empty":true},"size":20,"first":true,"numberOfElements":0,"empty":true}
=======================response end=================================================
When I disabled the debug interceptor it worked!
#Bean
public RestTemplate getRestTemplate(){
RestTemplate restTemplate = new RestTemplate();
if(apiRestDebugEnable){
restTemplate.setInterceptors(Collections.singletonList(new RequestResponseLoggingInterceptor()));
}
return restTemplate;
}
public class RequestResponseLoggingInterceptor implements ClientHttpRequestInterceptor {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
logRequest(request, body);
ClientHttpResponse response = execution.execute(request, body);
logResponse(response);
return response;
}
private void logRequest(HttpRequest request, byte[] body) throws IOException {
if (log.isDebugEnabled()) {
log.debug("===========================request begin================================================");
log.debug("URI : {}", request.getURI());
log.debug("Method : {}", request.getMethod());
log.debug("Headers : {}", request.getHeaders());
log.debug("Request body 1: {}", new String(body, "UTF-8"));
log.debug("==========================request end================================================");
}
}
private void logResponse(ClientHttpResponse response) throws IOException {
if (log.isDebugEnabled()) {
log.debug("============================response begin==========================================");
log.debug("Status code : {}", response.getStatusCode());
log.debug("Status text : {}", response.getStatusText());
log.debug("Headers : {}", response.getHeaders());
log.debug("Response body 2: {}", StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()));
log.debug("=======================response end=================================================");
}
}
}
I have a rest controller that requires the user to be authenticated. But when the test is run the response is always 401. I am using the "WithSecurityContext" annotation, but it does not work.
Annotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD })
#WithSecurityContext(factory = WithOAuth2AuthenticationSecurityContextFactory.class)
public #interface WithOAuth2Authentication {
String clientId() default "temporal";
String username() default "username";
String[] scopes() default { "read", "write", "trust" };
}
Class implementing annotation
public class WithOAuth2AuthenticationSecurityContextFactory implements WithSecurityContextFactory<WithOAuth2Authentication> {
#Override
public SecurityContext createSecurityContext(WithOAuth2Authentication annotation) {
Set<String> scopes = new HashSet<>();
Collections.addAll(scopes, annotation.scopes());
OAuth2Request oAuth2Request = new OAuth2Request(null, annotation.clientId(), null, true, scopes, null, null, null, null);
Authentication auth2Authentication = new OAuth2Authentication(oAuth2Request, new TestingAuthenticationToken(annotation.username(), null, "read"));
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(auth2Authentication);
return context;
}
}
Unit test
#Before
public void setup() {
this.mapper = new ObjectMapper();
RestDocumentationResultHandler document =
document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()));
this.mock = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation))
.apply(SecurityMockMvcConfigurers.springSecurity())
.alwaysDo(document)
.build();
}
#Test
#WithOAuth2Authentication
public void create() throws Exception {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Accept-Language", "en");
httpHeaders.add("Content-Type", "application/json");
httpHeaders.add("Accept", "application/json");
String JSON = this.mapper.writeValueAsString(new Register.Project());
this.mock.perform(post("/project/create")
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
.headers(httpHeaders)
.content(JSON))
.andDo(print())
.andExpect(status().isOk());
}
The result of the test
MockHttpServletResponse:
Status = 401
Error message = null
Headers = {X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate, no-store], Pragma=[no-cache, no-cache], Expires=[0], X-Frame-Options=[DENY], WWW-Authenticate=[Bearer realm="oauth2-resource", error="unauthorized", error_description="Full authentication is required to access this resource"], Content-Type=[application/json;charset=UTF-8]}
Content type = application/json;charset=UTF-8
Body = {"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
Forwarded URL = null
Redirected URL = null
Cookies = []
I'm building a RESTful authentication service and I'm having trouble connecting to it. I get the dreaded "Server returned HTTP response code: 400" when trying to retrieve the response. This seems very odd. I would think I'd get this error when sending the request.
I'm using this service as part of a Spring-Security AuthenticationProvider. I'm currently using a simulator instead of the actual service for testing. It won't connect to either the simulator or the service.
Here is the calling method:
public <T> T invoke(String service, Object request, Class<T> responseType) throws IOException {
ObjectMapper mapper = new ObjectMapper();
URL url = new URL("http://localhost:8888/simulator/rest" + service);
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
uc.setRequestMethod("POST");
uc.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
uc.setDoOutput(true);
uc.setDoInput(true);
uc.connect();
OutputStream out = uc.getOutputStream();
mapper.writeValue(out, request);
out.close();
return mapper.readValue(uc.getInputStream(), responseType);
}
Here is the code that calls this method:
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken("thomas", "thomas");
UsernamePasswordAuthenticationToken response =
invoke("/authenticate", token, UsernamePasswordAuthenticationToken.class);
Here is the simulator method that gets called:
#RequestMapping(value = "/authenticate", method = RequestMethod.POST)
#ResponseBody
public UsernamePasswordAuthenticationToken authenticate(
#RequestBody UsernamePasswordAuthenticationToken userNameAndPassword) {
String userName = (String) userNameAndPassword.getPrincipal();
String password = (String) userNameAndPassword.getCredentials();
if (userName.equalsIgnoreCase("thomas")) {
if (userName.equals(password)) {
UsernamePasswordAuthenticationToken response =
new UsernamePasswordAuthenticationToken(
userName,
password,
new ArrayList<GrantedAuthority>());
return response;
}
}
return new UsernamePasswordAuthenticationToken(userName, password);
}
The line that causes the error is the :
mapper.readValue(uc.getInputStream(), responseType);
If can't see any issues with this code. Must have been looking at it too long. Need new eyes on the problem.
BTW, this REST service and simulator has been used successfully with other operations.
Additional Information:
The error occurs in the uc.getInputStream() call. The HttpURLConnection.inputStream = null.
Also, the headers for the request are as follows:
If this helps, here are the headers for this request:
[WARN] 400 - POST /simulator/rest/authenticate (127.0.0.1) 1417 bytes
Request headers
Content-Type: application/json;charset=UTF-8
X-Tenant: 1
Authorization: 0000013770b132a1dfcbfe0a694542b244534e0e406cfa857660c904daa89af91d0ac769
Cache-Control: no-cache
Pragma: no-cache
User-Agent: Java/1.6.0_26
Host: localhost:8888
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 112
Response headers
Set-Cookie: JSESSIONID=1r02p7yvm8mzs;Path=/
X-UA-Compatible: IE=9
Content-Type: text/html; charset=iso-8859-1
Content-Length: 1417
Here is the my Token code:
import java.util.List;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
public class SerializedAuthenticationToken extends UsernamePasswordAuthenticationToken {
private static final long serialVersionUID = 2783395505630241326L;
private Object principal;
private Object credentials;
/**
* no-arg constructor to satisfy Serializable.
*/
public SerializedAuthenticationToken() {
super(null, null);
}
/**
* constructor.
*/
public SerializedAuthenticationToken(Object principal, Object credentials) {
super(null, null);
setPrincipal(principal);
setCredentials(credentials);
}
/**
* constructor with List<GrantedAuthorities>.
*/
public SerializedAuthenticationToken(Object principal, Object credentials, List<GrantedAuthority> authorities) {
super(null, null, authorities);
setPrincipal(principal);
setCredentials(credentials);
}
public Object getPrincipal() {
return principal;
}
public void setPrincipal(Object principal) {
this.principal = principal;
}
public Object getCredentials() {
return credentials;
}
public void setCredentials(Object credentials) {
this.credentials = credentials;
}
public void setName(Object name) {
}
}
I also now am getting a new stack trace:
org.codehaus.jackson.map.JsonMappingException: Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead (through reference chain: com.mckesson.shared.util.SerializedAuthenticationToken["authenticated"])
You need to create a Dummy token to do that. Here is my test to verify it:
public class JacksonTest {
#Test
public void test() throws Exception {
ObjectMapper mapper = new ObjectMapper();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("thomas", "thomas");
String tokenStr = mapper.writeValueAsString(token);
/* WON'T WORK
UsernamePasswordAuthenticationToken auth = mapper.readValue(tokenStr, UsernamePasswordAuthenticationToken.class);
*/
MyToken auth = mapper.readValue(tokenStr, MyToken.class);
String authStr = mapper.writeValueAsString(auth);
Assert.assertThat(tokenStr.equals(authStr), is(true));
}
private static class MyToken extends UsernamePasswordAuthenticationToken {
private Object principal;
private Object credentials;
private static final long serialVersionUID = -5045038656629236029L;
public MyToken() {
super(null, null);
}
public MyToken(Object principal, Object credentials) {
super(null, null);
this.principal = principal;
this.credentials = credentials;
}
/**
* #return the principal
*/
public Object getPrincipal() {
return principal;
}
/**
* #param principal the principal to set
*/
public void setPrincipal(Object principal) {
this.principal = principal;
}
/**
* #return the credentials
*/
public Object getCredentials() {
return credentials;
}
/**
* #param credentials the credentials to set
*/
public void setCredentials(Object credentials) {
this.credentials = credentials;
}
public void setName(Object name) {
}
}
}
Can't spot the exact issue, but I think a contributor to the problem is that fact that you're unnecessarily dropping down to such a low-level API to accomplish something for which some very reasonable abstractions exist. Have a look at Spring's RestTemplate to see a more elegant means or writing client code against a RESTful service. Whatever you're doing wrong, chances are RestTemplate will get it right.