how to get Oauth2 Authorization Token using RestAssured - java

public static Response getCode() {
String authorization = encode(username, password);
return
given()
.header("authorization", "Basic " + authorization)
.contentType(ContentType.URLENC)
.formParam("response_type", "code")
.queryParam("client_id", clientId)
.queryParam("redirect_uri", redirectUri)
.queryParam("scope", scope)
.post("/oauth2/authorize")
.then()
.statusCode(200)
.extract()
.response();
}
public static String parseForOAuth2Code(Response response) {
return response.jsonPath().getString("code");
}
#BeforeAll
public static void setup() {
RestAssured.baseURI = "https://some-url.com";
}
#Test
public void iShouldGetCode() {
Response response = getCode();
String code = parseForOAuth2Code(response);
Assertions.assertNotNull(code);
}
}
response is null therefore keeps throwing cannot parse json

Related

MockMVC content is missing

The content from MockMvc managed to have status code but missing the data.
Test class:
#Test
public void shouldReturnAll() throws Exception {
when(userService.getAll()).thenReturn(users); // note that 'users' is not empty, I already checked.
MvcResult response = this.mvc.perform(get("/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data", hasSize(2)))
.andReturn();
}
Reponse:
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json"]
Content type = application/json
Body = {"status":"success"}
Forwarded URL = null
Redirected URL = null
Cookies = []
I think it has something to do with Response object in my Controller class.
Controller:
#GetMapping
public ResponseEntity<Response> getAll() {
List<User> users = userService.getAll();
Response resp = new Response(StatusMessage.SUCCESS, users);
return new ResponseEntity<Response>(resp, HttpStatus.OK);
}
Edit: Another test which works (getting a single User):
#Test
public void getUserByIdTest() throws Exception {
when(this.userService.getUserById(any(Long.class))).thenReturn(user);
MvcResult response = this.mvc.perform(get("/users/{id}", user.getId()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.id", is(user.getId().intValue())))
.andExpect(jsonPath("$.data.name", is(user.getName())))
.andReturn();
}
Controller:
#GetMapping(value = "/{id}")
public ResponseEntity<Response> getUserById(#PathVariable Long id) throws Exception {
try {
User user = userService.getUserById(id);
Response resp = new Response(StatusMessage.SUCCESS, user);
return new ResponseEntity<Response>(resp, HttpStatus.OK);
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
Response object class:
#Data
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Response<T> {
private String status;
private T data;
public Response(StatusMessage status, T data) {
this.status = status.getStatusMessage();
this.data = data;
}

Provide different ClientInterceptor per request using Spring Web Services

I've created a custom web service client by extending WebServiceGatewaySupport and also implement custom ClientInterceptor to log some request/response data.
I have to create new interceptor for every call because it has to store some data about the request.
The problem occurs when I make two or more calls to my client. The first request applies its own interceptor with its clientId. The second should do the same. But since both requests use the same WebServicetemplate in my client, the second request replaces the interceptor with its own, with its clientId there.
As a result, I should get the following output to the console:
Request: clientId-1
Request: clientId-2
Response: clientId-1
Response: clientId-2
But I got this:
Request: clientId-1
Request: clientId-2
Response: clientId-2
Response: clientId-2
Here is come code examples (just for understanding how it should work):
#Data
class Response {
private final String result;
public Response(String result) {
this.result = result;
}
}
#Data
class Request {
private final String firstName;
private final String lastName;
}
#Data
class Context {
private final String clientId;
}
#Data
class Client {
private final String clientId;
private final String firstName;
private final String lastName;
}
class CustomInterceptor extends ClientInterceptorAdapter {
private final String clientId;
public CustomInterceptor(String clientId) {
this.clientId = clientId;
}
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
System.out.println("Request: " + clientId);
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
System.out.println("Response: " + clientId);
return true;
}
#Override
public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
System.out.println("Error: " + clientId);
return true;
}
}
#Component
class CustomClient extends WebServiceGatewaySupport {
public Response sendRequest(Request request, Context context) {
CustomInterceptor[] interceptors = {new CustomInterceptor(context.getClientId())};
setInterceptors(interceptors);
return (Response) getWebServiceTemplate().marshalSendAndReceive(request);
}
}
#Service
#RequiredArgsConstructor
class CustomService {
private final CustomClient customClient;
public String call(Request request, Context context) {
Response response = customClient.sendRequest(request, context);
return response.getResult();
}
}
#RestController
#RequestMapping("/test")
#RequiredArgsConstructor
class CustomController {
private final CustomService service;
public CustomController(CustomService service) {
this.service = service;
}
#PostMapping
public String test(#RequestBody Client client) {
Request request = new Request(client.getFirstName(), client.getLastName());
Context context = new Context(client.getClientId());
return service.call(request, context);
}
}
Is it possible to implement custom interceptors with some state for each call? Preferably without any locks on WebServicetemplate to avoid performance degradation.
Okay. I've found the solution for my case.
I've created an implementation of WebServiceMessageCallback and using it I'm saving data of each request not in interceptor but in WebServiceMessage's mime header.
#Data
class CustomMessageCallback implements WebServiceMessageCallback {
private final String clientId;
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
MimeHeaders headers = ((SaajSoapMessage) message).getSaajMessage().getMimeHeaders();
headers.addHeader("X-Client-Id", clientId);
}
}
And pass this callback in my client implementation:
#Component
class CustomClient extends WebServiceGatewaySupport {
public Response sendRequest(Request request, Context context) {
CustomInterceptor[] interceptors = {new CustomInterceptor()};
setInterceptors(interceptors);
return (Response) getWebServiceTemplate()
.marshalSendAndReceive(request, new CustomMessageCallback(context.getClientId()));
}
}
So now I can get this data while processing request/response/error via interceptor.
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
String clientId = ((SaajSoapMessage) messageContext.getRequest())
.getSaajMessage()
.getMimeHeaders()
.getHeader("X-Client-Id")[0];
System.out.println("Request: " + clientId);
return true;
}

How to add basic auth using retrofit2?

I already search all around the places on internet how to add basic auth using retrofit2 but still no luck. I already implemented a simple login mechanism but basic auth must be use in order for successful login.
My model
public class ResObj {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
ApiUtils.java
public class ApiUtils {
public static final String BASE_URL = "xxxxx";
public static UserService getUserService(){
return RetrofitClient.getClient(BASE_URL).create(UserService.class);
}
}
RetrofitClient.java
public class RetrofitClient {
private static Retrofit retrofit = null;
public static Retrofit getClient(String url){
if(retrofit == null){
retrofit = new Retrofit.Builder().baseUrl(url).addConverterFactory(GsonConverterFactory.create()).build();
}
return retrofit;
}
}
UserService.java
public interface UserService {
#GET("login?username={username}&password={password}")
Call<ResObj> login(#Path("username") String username, #Path("password") String password);
}
LoginActivity.java
private void doLogin(String username, String password){
Call<ResObj> call = userService.login(username, password);
call.enqueue(new Callback<ResObj>() {
#Override
public void onResponse(Call<ResObj> call, Response<ResObj> response) {
if(response.isSuccessful()){
ResObj resObj = response.body();
if(resObj.getMessage().equals("true")){
Intent intent = new Intent(TextLoginActivity.this, MainActivity.class);
startActivity(intent);
} else {
Toast.makeText(TextLoginActivity.this, "The username and password is incorrect", Toast.LENGTH_SHORT).show();
}
} else{
Toast.makeText(TextLoginActivity.this, "Error! Please try again!", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<ResObj> call, Throwable t) {
Toast.makeText(TextLoginActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
As I see your login API request, it exposes the user's password and the username in the query. The least you can do is maybe encrypt it. But a much better solution would be to have a POST request instead of a GET request with the username and password in the body. You add the Authorization header to the API request as follows:
public interface UserService {
String authorization = "Authorization: Basic XXXXXX";
String contentType = "Content-Type: application/json";
// Static Header
#POST("login")
#Headers({
contentType,
authorization
})
Call<ResObj> login(#Body UserCredential userCred);
// Dynamic Header
#POST("login")
Call<ResObj> login(#Header("Authorization") String basicAuth, #Body UserCredential userCred);
}
The Body model can be:
public class UserCredential {
private String username, password;
public String getPassword() {
...encrypt your password here...
return encrypted_password;
}
}
Also, it is safe to put a null check before you do this: response.body(); in the API response.

Propagate HTTP header (JWT Token) over services using spring rest template

I have a microservice architecture, both of them securized by spring security an JWT tokens.
So, when I call my first microservice, I want to take the JWT token and send a request to another service using those credentials.
How can I retrieve the token and sent again to the other service?
Basically your token should be located in the header of the request, like for example: Authorization: Bearer . For getting it you can retrieve any header value by #RequestHeader() in your controller:
#GetMapping("/someMapping")
public String someMethod(#RequestHeader("Authorization") String token) {
}
Now you can place the token within the header for the following request:
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", token);
HttpEntity<RestRequest> entityReq = new HttpEntity<RestRequest>(request, headers);
Now you can pass the HttpEntity to your rest template:
template.exchange("RestSvcUrl", HttpMethod.POST, entityReq, SomeResponse.class);
Hope I could help
I've accomplished the task, creating a custom Filter
public class RequestFilter implements Filter{
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader(RequestContext.REQUEST_HEADER_NAME);
if (token == null || "".equals(token)) {
throw new IllegalArgumentException("Can't retrieve JWT Token");
}
RequestContext.getContext().setToken(token);
chain.doFilter(request, response);
}
#Override
public void destroy() { }
#Override
public void init(FilterConfig arg0) throws ServletException {}
}
Then, setting in my config
#Bean
public FilterRegistrationBean getPeticionFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new RequestFilter());
registration.addUrlPatterns("/*");
registration.setName("requestFilter");
return registration;
}
With that in mind, I've create another class with a ThreadLocal variable to pass the JWT token from the Controller to the Rest Templace interceptor
public class RequestContext {
public static final String REQUEST_HEADER_NAME = "Authorization";
private static final ThreadLocal<RequestContext> CONTEXT = new ThreadLocal<>();
private String token;
public static RequestContext getContext() {
RequestContext result = CONTEXT.get();
if (result == null) {
result = new RequestContext();
CONTEXT.set(result);
}
return result;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
public class RestTemplateInterceptor implements ClientHttpRequestInterceptor{
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
String token = RequestContext.getContext().getToken();
request.getHeaders().add(RequestContext.REQUEST_HEADER_NAME, token);
return execution.execute(request, body);
}
}
Add interceptor to the config
#PostConstruct
public void addInterceptors() {
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
interceptors.add(new RestTemplateInterceptor());
restTemplate.setInterceptors(interceptors);
}
I think it is better to add the interceptor specifically to the RestTemplate, like this:
class RestTemplateHeaderModifierInterceptor(private val authenticationService: IAuthenticationService) : ClientHttpRequestInterceptor {
override fun intercept(request: org.springframework.http.HttpRequest, body: ByteArray, execution: ClientHttpRequestExecution): ClientHttpResponse {
if (!request.headers.containsKey("Authorization")) {
// don't overwrite, just add if not there.
val jwt = authenticationService.getCurrentUser()!!.jwt
request.headers.add("Authorization", "Bearer $jwt")
}
val response = execution.execute(request, body)
return response
}
}
And add it to the RestTemplate like so:
#Bean
fun restTemplate(): RestTemplate {
val restTemplate = RestTemplate()
restTemplate.interceptors.add(RestTemplateHeaderModifierInterceptor(authenticationService)) // add interceptor to send JWT along with requests.
return restTemplate
}
That way, every time you need a RestTemplate you can just use autowiring to get it. You do need to implement the AuthenticationService still to get the token from the TokenStore, like this:
val details = SecurityContextHolder.getContext().authentication.details
if (details is OAuth2AuthenticationDetails) {
val token = tokenStore.readAccessToken(details.tokenValue)
return token.value
}
May be a little bit late but I think this is a common question, regarding
Spring Security 6.0.0 for web client there is a class called ServletBearerExchangeFilterFunction that you can use to read the token from the security context and inject it.
#Bean
public WebClient rest() {
return WebClient.builder()
.filter(new ServletBearerExchangeFilterFunction())
.build();
For RestTemplate there is no automatic way and is recommended use a filter
#Bean
RestTemplate rest() {
RestTemplate rest = new RestTemplate();
rest.getInterceptors().add((request, body, execution) -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return execution.execute(request, body);
}
if (!(authentication.getCredentials() instanceof AbstractOAuth2Token)) {
return execution.execute(request, body);
}
AbstractOAuth2Token token = (AbstractOAuth2Token) authentication.getCredentials();
request.getHeaders().setBearerAuth(token.getTokenValue());
return execution.execute(request, body);
});
return rest;
}

How can I send my own response in spring?

I am implementing Spring security with JWT in my application and when ever an unauthorized call is made it returns the following response
#Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
The response json look like below
{
"timestamp": 1497832267379,
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/path"
}
Instead of this can I sent my own custom response something like:
{
"code":401,
"message":"The request is unauthorized"
}
Any help is appreciated
EDIT
I updated the code to below format:
#Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
//response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
Status unauthorizedEntry = new Status();
unauthorizedEntry.setCode(401);
unauthorizedEntry.setMessage("Unauthorized Entry");
Map<String, Object> unauthorizedEntryResponse = new HashMap<>();
unauthorizedEntryResponse.put("status", unauthorizedEntry);
objectMapper.writeValue(response.getOutputStream(), unauthorizedEntry);
response.flushBuffer();
}
My Status class is below:
public class Status {
int code;
String message;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Now I am getting a 200 response but nothing is shown in the screen. It is fully blank. Any help is appreciated!
You can try to add a controller advice
#RestController
#ControllerAdvice
public class ExceptionHandlerController {
#ExceptionHandler(UsernameNotFoundException.class, DataAccessException.class)
#ResponseStatus(HttpStatus.SC_UNAUTHORIZED)
#ResponseBody ErrorInfo
UnauthorizeExceptionInfo(HttpServletRequest req, Exception ex) {
return new ErrorInfo(req.getRequestURL(), ex);
}
}
and ErrorInfo.class
#JsonIgnore
public final StringBuffer url;
public final String ex;
public ErrorInfo(StringBuffer stringBuffer, Exception ex) {
this.url = stringBuffer;
this.ex = ex.getLocalizedMessage();
}
and when you will throw a new UsernameNotFoundException the controller will handle the response.
And I suppose that the exceptions are throw in your #Override public loadUserByUsername from CustomUserDetailsService if the password/email don't match.
More details here: https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
This ought to work for you:
#Autowired
private ObjectMapper objectMapper;
#Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
// notify client of response body content type
response.addHeader("Content-Type", "application/json;charset=UTF-8");
// set the response status code
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
// set up the response body
Status unauthorized = new Status(HttpServletResponse.SC_UNAUTHORIZED,
"The request is unauthorized");
// write the response body
objectMapper.writeValue(response.getOutputStream(), unauthorized);
// commit the response
response.flushBuffer();
}
public class Status {
private int code;
private String message;
public Status(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
Note that you need

Categories