I am trying get access-token. By using curl/commnad window i can get it but i need to use android volley. Volley shows error says unexpected response code. Here is curl command:
$ curl "https://api.*****.com/auth/oauth/v2/token" \
--insecure \
--header "Accept: application/json" \
--header "Content-Type: application/x-www-form-urlencoded" \
--data "grant_type=authorization_code&code=9d40008a9a4-438b-800c-dec6486a7631"\
--data
“client_id=l7xxdedc2c3d49e58459287fe66092ad&client_secret=fa48a352e633841695b1d969750" \
--data "scope=scope_test&state=state_test" \
--data "redirect_uri=http%3A%2F%2Flocalhost%3A3000" \
--request POST$
I have written an equivalent Volley string request. I dont know where is my mistake? Why it is not giving me the response. Please help me
StringRequest postRequest=new StringRequest(Request.Method.POST,url,new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.d("accessToken:",response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
Log.d("error:",volleyError.toString());
}
}){
#Override
public byte[] getPostBody() throws AuthFailureError {
String grantcode=preference.getAccessToken();
String httpPostBody="grant_type=authorization_code&code="+grantcode+"&client_id=l7xx5a227281eb364ab3bb6fd8cc49648427&client_secret=c29a24fad90640f993770f07a5e77892&scope=scope_test&state=state_test";
return httpPostBody.getBytes();
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String,String> headers=new HashMap<String,String>();
headers.put("Accept","application/json");
headers.put("Content-Type","application/x-www-form-urlencoded");
return headers;
}
};
ApplicationController.getInstance().addToRequestQueue(postRequest);
}
});
Even i tried to use getBody instead of getPostBody. But it did not solve my problem. thanks again
I solved this problem. Here is the way:
StringRequest postRequest=new StringRequest(Request.Method.POST,url,new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.d("accessToken:",response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
Log.d("error:",volleyError.toString());
}
}){
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> params=new HashMap<String,String>();
params.put("grant_type","authorization_code");
params.put("code",preference.getGrantCode());
params.put("client_id", Resources.CLIENT_ID);
params.put("client_secret",Resources.CLIENT_SECRET);
params.put("scope","scope_test");
params.put("state","state_test");
params.put("redirect_uri",Resources.REDIRECT_URI );
return params;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String,String> headers=new HashMap<String,String>();
headers.put("Accept","application/json");
headers.put("Content-Type","application/x-www-form-urlencoded");
return headers;
}
};
ApplicationController.getInstance().addToRequestQueue(postRequest);
I ran into similar issue.
Putting --data values in to body solved for me.
Related
I am working on a spring boot application. I want to modify the response of the request by request body field "Id".
I have implemented below, but still getting just the name in the output while implementing.Any suggestions on implementing below would be helpful:
Below is the requestBody:
{
"id" : "123"
}
In response, I want to append that field to response id(fieldname from request body).
responseBody:
{
"name" : "foo123" //name + id from request
}
MyCustomFilter:
public class TestFilter implements Filter {
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
final PrintStream ps = new PrintStream(baos);
MultiReadHttpServletRequest wrapper = new MultiReadHttpServletRequest((HttpServletRequest) request);
MyRequestWrapper req = new MyRequestWrapper(wrapper);
String userId = req.getId();
chain.doFilter(wrapper, new HttpServletResponseWrapper(res) {
#Override
public ServletOutputStream getOutputStream() throws IOException {
return new DelegatingServletOutputStream(new TeeOutputStream(super.getOutputStream(), ps)
);
}
#Override
public PrintWriter getWriter() throws IOException {
return new PrintWriter(new DelegatingServletOutputStream(new TeeOutputStream(super.getOutputStream(), ps))
);
}
});
String responseBody = baos.toString();
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(responseBody);
String name = node.get("name").astext();
((ObjectNode) node1).put("name", name + userId);
chain.doFilter(wrapper, res);
}
MyRequestWrapper:
public class MyRequestWrapper extends HttpServletRequestWrapper {
private ServletInputStream input;
public MyRequestWrapper(ServletRequest request) {
super((HttpServletRequest)request);
}
public String getId() throws IOException {
if (input == null) {
try {
JSONObject jsonObject = new JSONObject(IOUtils.toString(super.getInputStream()));
String userId = jsonObject.getString("id");
userId = userId.replaceAll("\\D+","");
return userId;
} catch (JSONException e) {
e.printStackTrace();
}
}
return null;
}
}
MultiReadHttpServletRequest.java
public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
private byte[] body;
public MultiReadHttpServletRequest(HttpServletRequest request) {
super(request);
try {
body = IOUtils.toByteArray(request.getInputStream());
} catch (IOException ex) {
body = new byte[0];
}
}
#Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream(), getCharacterEncoding()));
}
#Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStream() {
ByteArrayInputStream wrapperStream = new ByteArrayInputStream(body);
#Override
public boolean isFinished() {
return false;
}
#Override
public boolean isReady() {
return false;
}
#Override
public void setReadListener(ReadListener readListener) {
}
#Override
public int read() throws IOException {
return wrapperStream.read();
}
};
}
}
Any suggestions are appreciated. TIA.
Nte: After update i am not able to see the updated response as output. I am still seeing just the name but not id appended to it.
The one issue I see with your own implementation of ServletRequest is that you call super.getInputStream() instead of request.getInputStream(). Your own request is empty by default, that's why you're getting time out exception. You have to delegate a call to the actual request:
public class MyRequestWrapper extends HttpServletRequestWrapper {
private ServletInputStream input;
public MyRequestWrapper(ServletRequest request) {
super((HttpServletRequest)request);
}
public String getId() throws IOException {
if (input == null) {
try {
JSONObject jsonObject = new JSONObject(IOUtils.toString(/*DELETEGATE TO ACTUAL REQUEST*/request.getInputStream()));
String userId = jsonObject.getString("id");
userId = userId.replaceAll("\\D+","");
return userId;
} catch (JSONException e) {
e.printStackTrace();
}
}
return null;
}
}
While migrating my spring server from servlets to reactive I had to change all the filters in the code to WebFilter. One of the filters was decompressing gzipped content, but I couldn't do the same with the new WebFilter.
With servlets I wrapped the inputstream with a GzipInputStream. What is the best practice to do it with spring reactive?
Solution:
#Component
public class GzipFilter implements WebFilter {
private static final Logger LOG = LoggerFactory.getLogger(GzipFilter.class);
public static final String CONTENT_ENCODING = "content-encoding";
public static final String GZIP = "gzip";
public static final String UTF_8 = "UTF-8";
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
if (!isGzip(request)) {
return chain.filter(exchange);
}
else {
ServerHttpRequest mutatedRequest = new ServerHttpRequestWrapper(request);
ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();
return chain.filter(mutatedExchange);
}
}
private boolean isGzip(ServerHttpRequest serverHttpRequest) {
String encoding = serverHttpRequest.getHeaders().getFirst(CONTENT_ENCODING);
return encoding != null && encoding.contains(GZIP);
}
private static class ServerHttpRequestWrapper implements ServerHttpRequest {
private ServerHttpRequest request;
public ServerHttpRequestWrapper(ServerHttpRequest request) {
this.request = request;
}
private static byte[] getDeflatedBytes(GZIPInputStream gzipInputStream) throws IOException {
StringWriter writer = new StringWriter();
IOUtils.copy(gzipInputStream, writer, UTF_8);
return writer.toString().getBytes();
}
#Override
public String getId() {
return request.getId();
}
#Override
public RequestPath getPath() {
return request.getPath();
}
#Override
public MultiValueMap<String, String> getQueryParams() {
return request.getQueryParams();
}
#Override
public MultiValueMap<String, HttpCookie> getCookies() {
return request.getCookies();
}
#Override
public String getMethodValue() {
return request.getMethodValue();
}
#Override
public URI getURI() {
return request.getURI();
}
#Override
public Flux<DataBuffer> getBody() {
Mono<DataBuffer> mono = request.getBody()
.map(dataBuffer -> dataBuffer.asInputStream(true))
.reduce(SequenceInputStream::new)
.map(inputStream -> {
try (GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream)) {
byte[] targetArray = getDeflatedBytes(gzipInputStream);
return new DefaultDataBufferFactory().wrap(targetArray);
}
catch (IOException e) {
throw new IllegalGzipRequest(String.format("failed to decompress gzip content. Path: %s", request.getPath()));
}
});
return mono.flux();
}
#Override
public HttpHeaders getHeaders() {
return request.getHeaders();
}
}
}
love #Yuval's solution!
My original idea was to convert Flux to a local file, and then decompress the local file.
But getting a file downloaded in Spring Reactive is too challenging. I googled a lot, and most of them are blocking way to get file, (e.g. Spring WebClient: How to stream large byte[] to file? and How to correctly read Flux<DataBuffer> and convert it to a single inputStream , none of them works...) which makes no sense and will throw error when calling block() in a reactive flow.
#Yuval saved my day! It works well for me!
ctually when we call API and send request in JSON format we are expecting response also come into JSON format. But here back end team sending me response in String format therefore my onErrorResponse () method get called. Here my status code is 200. But due to format of response not executed onResponse () method. So will you please help me to handle this? Might be I have to use CustomRequest here. Any suggestoin will be appreciated. Thanks
public class SampleJsonObjTask {
public static ProgressDialog progress;
private static RequestQueue queue;
JSONObject main;
JsonObjectRequest req;
private MainActivity context;
private String prd,us,ver,fha,ve,ves,sz,cat,pa,h,t,en,pha,pur,dip;
public SampleJsonObjTask(MainActivity context, JSONObject main) {
progress = new ProgressDialog(context);
progress.setMessage("Loading...");
progress.setCanceledOnTouchOutside(false);
progress.setCancelable(false);
progress.show();
this.context = context;
this.main = main;
ResponseTask();
}
private void ResponseTask() {
if (queue == null) {
queue = Volley.newRequestQueue(context);
}
req = new JsonObjectRequest(Request.Method.POST, "", main,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
progress.dismiss();
Log.e("response","response--->"+response.toString());
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
progress.dismiss();//error.getMessage()
/*back end team sending me response in String format therefore my onErrorResponse () method get called. Here my status code is 200.*/
}
})
{
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json");
return params;
}
};
req.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 0, 1f));
queue.add(req);
}
}
Here the Response coming like string format that is Value OK,
com.android.volley.ParseError: org.json.JSONException: Value OK of type java.lang.String cannot be converted to JSONObject
You can use StringRequest for that:
StringRequest request = new StringRequest(StringRequest.Method.POST, url, new Response.Listener<String>() {
#Override
public void onResponse(String response) { }
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
#Override
public String getBodyContentType() {
return "application/json; charset=utf-8";
}
#Override
public byte[] getBody() {
try {
JSONObject jsonObject = new JSONObject();
/* fill your json here */
return jsonObject.toString().getBytes("utf-8");
} catch (Exception e) { }
return null;
}
};
Authorization Server using jdbc token store
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter {
#Autowired
private DataSource dataSource;
private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
#Autowired
private AuthenticationManager authenticationManager;
#Bean
public JdbcTokenStore tokenStore(){
return new JdbcTokenStore(dataSource);
}
#Bean
protected AuthorizationCodeServices authorizationCodeServices(){
return new JdbcAuthorizationCodeServices(dataSource);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(new JdbcTokenStore(dataSource))
.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.passwordEncoder(passwordEncoder);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
Resource Server
private static final String RESOURCE_ID = "test";
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends
ResourceServerConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private TokenStore tokenStore;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore).resourceId(RESOURCE_ID);
}
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}
}
ResourceController
#RequestMapping(value = "/transaction", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces =MediaType.APPLICATION_JSON_VALUE)
public Map<String, Object> haloApi(#RequestBody(required = false) Map<String, String> input){Map<String, Object> data = new HashMap<>();
data.put("kwame", new Date());
if(input != null){
String name = input.get("test");
if(name != null && !name.isEmpty()){
data.put("nice", "Hello "+name);
}
}
return data;
}
When I send a post request via cURL to Authorizer Server, I got a token in return but unfortunately when I use it in the Resource server it always return
invalid token as response.
What am I missing?
See below my sample request for both Authorizer and Resource Server and advise please.
Authorizer Server:
$ curl -X POST -H "Accept: application/json" -d "grant_type=client_credentials" -u "daniel:123456" "http://localhost:5600/oauth/token"
Sample response:
{
"access_token": "cddc1b75-87d9-4a2f-9d66-210eae85b0f9",
"token_type": "bearer",
"expires_in": 149,
"scope": "read write"
}
When I use the token immediately as seen in the snippet below,
curl -X POST http://localhost:5700/checkout/transaction -v -H 'Content-Type: application/json' -H 'Authorization: Bearer ac72b34f-437d-4134-8760-16f1ca3f0483' -d '{"test": "test"}'
I constantly get the following response:
{
"error": "invalid_token",
"error_description": "ac72b34f-437d-4134-8760-16f1ca3f0483"
}
Below is my log:
2017-01-08 18:45:36.375 DEBUG 11012 --- [io-22000-exec-1] o.s.s.oauth2.client.OAuth2RestTemplate : GET request for "ip&port/oauth/check_token" resulted in 401 (null); invoking error handler
Here, my application.properties for resource server:
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxxxxx
spring.datasource.username=xxxxxxxxx
spring.datasource.password=xxxxxxxxx
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.database-platform = org.hibernate.dialect.MySQL5Dialect
server.port=5700
security.oauth2.resource.user-info-uri=localhost:5600/oauth/check_token
logging.level.org.springframework.security=DEBUG
Thanks all...i finally discovered the issue!
Inside the application.properties i introduced
security.oauth2.resource.user-info-uri=http://localhost:5600/oauth/check_token
so I simply had to remove that line to fix the issue.
Special thanks to Cleto for your attention and contribution.
You are using quotes before and after your token, please do the request without quotes -H "Authorization: Bearer ac72b34f-437d-4134-8760-16f1ca3f0483"
And please be sure that your token is still valid at the request time. ( see access_token_validity column on oauth_client_details table)
Edit:
I created a blank project with exactly the SAME authorization and resource server as yours, And I was able to make the request using:
curl -X POST localhost:****/transaction -v -H
"Content-Type: application/json" -H "Authorization: Bearer
4c7591de-a4bc-4896-afc4-ed1fb7199c06" -d "{\"test\": \"test\"}"
Please use just double quotes on every parameter ( -H, -d ) and escape the double quotes of your data with .
When I was testing, I realized that you didn't set the resource_id of your resources:
resources.tokenStore(tokenStore).resourceId(RESOURCE_ID);
After set the ID, be sure that your database has a correct oauth_client_details with access to the resource.
If you check that everything looks OK but it's not working, please debug into spring API and see exactly the error.
I am attempting to change the Content-Type header in a request and change it to "application/json" before it reaches my spring rest controller. I have created a servlet request wrapper to change the values, but when the request reaches the controller it is still "text/plain". The logging shows that the header value has been changed before hitting doFilter();
Here is my class extending HttpServletRequestWrapper
class HttpServletRequestWritableWrapper extends HttpServletRequestWrapper {
private final Logger logger = org.slf4j.LoggerFactory.getLogger(HttpServletRequestWritableWrapper.class);
private final ByteArrayInputStream decryptedBody;
HttpServletRequestWritableWrapper(HttpServletRequest request, byte[] decryptedData) {
super(request);
decryptedBody = new ByteArrayInputStream(decryptedData);
}
#Override
public String getHeader(String name) {
String headerValue = super.getHeader(name);
if("Accept".equalsIgnoreCase(name))
{
logger.debug("Accept header changing :");
return headerValue.replaceAll(
MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_JSON_VALUE
);
}
else if ("Content-Type".equalsIgnoreCase(name))
{
logger.debug("Content type change: ");
return headerValue.replaceAll(MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_JSON_VALUE);
}
return headerValue;
}
#Override
public Enumeration<String> getHeaderNames() {
return super.getHeaderNames();
}
#Override
public String getContentType() {
String contentTypeValue = super.getContentType();
if (MediaType.TEXT_PLAIN_VALUE.equalsIgnoreCase(contentTypeValue)) {
logger.debug("Changing on getContentType():");
return MediaType.APPLICATION_JSON_VALUE;
}
return contentTypeValue;
}
#Override
public BufferedReader getReader() throws UnsupportedEncodingException {
return new BufferedReader(new InputStreamReader(decryptedBody, UTF_8));
}
#Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStream() {
#Override
public int read() {
return decryptedBody.read();
}
};
}
And here is my filter:
#WebFilter(displayName = "EncryptionFilter", urlPatterns = "/*")
public class EncryptionFilter implements Filter {
private final Logger logger = org.slf4j.LoggerFactory.getLogger(EncryptionFilter.class);
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
byte[] data = "{\"currentClientVersion\":{\"majorElement\":\"1\",\"minorElement\":\"2\"}}".getBytes();
logger.debug("data string " + data.toString());
logger.debug("Content-type before: " + servletRequest.getContentType());
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletRequestWritableWrapper requestWrapper = new HttpServletRequestWritableWrapper(request, data);
//logger.debug("Accept Header: " + requestWrapper.getHeader("Accept"));
//logger.debug("Content-Type: " + requestWrapper.getHeader("Content-Type"));
//logger.debug("Contenttype" + requestWrapper.getContentType());
filterChain.doFilter(requestWrapper, servletResponse);
}
#Override
public void destroy() {
}
}
It appears that the getHeaders method was being called somewhere else after my filter and not returning the headers with my updated values.
I added this override in my HttpServletRequestWrapper and it is now working:
#Override
public Enumeration<String> getHeaders(String name) {
List<String> headerVals = Collections.list(super.getHeaders(name));
int index = 0;
for (String value : headerVals) {
if ("Content-Type".equalsIgnoreCase(name)) {
logger.debug("Content type change: ");
headerVals.set(index, MediaType.APPLICATION_JSON_VALUE);
}
index++;
}
return Collections.enumeration(headerVals);
}