Get UsernamePasswordCredential from HttpServletRequest - servlets

I'm using Spring SimpleFormController to handle a form submission . when client sets UsernamePasswordCredentials in httpclient by httpclient.getState().setCredentials on Preemptive Authentication, how can i get this UsernamePasswordCredential in server side from HttpServletRequest inside my "onSubmit" method.

we can take the 'authorization' header value from the request and Base64-decodes the value to show that the username and password are in fact very easily readable if someone intercepts the network communication. (This is why it's a very good idea to use HTTPS for authentication).
try {
String auth = request.getHeader("Authorization");
if(auth!=null&&auth.length()>6){
String userpassEncoded = auth.substring(6);
// Decode it, using any base 64 decoder
sun.misc.BASE64Decoder dec = new sun.misc.BASE64Decoder();
String userpassDecoded= new String(dec.decodeBuffer(userpassEncoded));
System.out.println(" userpassDecoded = "+userpassDecoded);
}
} catch (Exception e) {
e.printStackTrace();
}

Related

FCM throws 401 after some time for data message notification

I am sending data message notification from Java app server to the FCM rest endpoint. Everything works fine, data messages are received by the app without any issues, however after some time (without any noticeable trend) , FCM stars returning 401. I am using Apache common's HTTPClient library to make the http callss. This is the relevant code snippet
final HttpPost httpPost = new HttpPost("https://fcm.googleapis.com/v1/projects/proj1/messages:send");
httpPost.setHeader("Content-Type", "application/json");
httpPost.setHeader("Authorization", "Bearer "+ accessToken);
responseBody = httpclient.execute(httpPost, responseHandler);
And this snippet is for getting the access token for API authorization
static{
FileInputStream refreshToken = null;
refreshToken = new FileInputStream("C:/prj/proserviceaccoutkey.json");
googleCredentials=GoogleCredentials.fromStream(refreshToken).createScoped("https://www.googleapis.com/auth/firebase.messaging");
options = new FirebaseOptions.Builder() .setCredentials(googleCredentials).build();
}
// Gets called each time a data message needs to be sent
public static synchronized String getAccessToken()
{
if(googleCredentials.getAccessToken()==null)
try {
googleCredentials.refresh();
} catch (IOException e) {
e.printStackTrace();
}
return googleCredentials.getAccessToken().getTokenValue();
}
looks like googleCredentials.getAccessToken() will always return non-null, even when the cahce token in no longer valid, and this is why token was not getting refreshed in the code. Applied the following fix, and it's working now.
public static synchronized String getAccessToken()
{
if(googleCredentials!=null)
try {
googleCredentials.refresh();
} catch (IOException e) {
e.printStackTrace();
}
return googleCredentials.getAccessToken().getTokenValue();
}
Though, it's not really utilizing the cached token, as every time it will be refreshing the token, but my issue has been rsolved for now.

Spring OAuth2 - URL Encoded of username within Basic auth header for token endpoint

It seems that Basic Auth for token endpoint is not compliant with OAuth2.0 spec, which specifies that the username and password should be URL encoded before being joined with a colon and the base 64 encoded. See rfc6749
Creation of the header should be:
auth_header = 'Basic' + base64 (urlEncode (client_id) + ':' + urlEncode (client_secret))
Hence there needs a URL decode step after splitting the username from the password.
Spring is just using the BasicAuthenticationFilter to extract the credentials and there doesn't seem a way of extending this to add the URL Decode step.
So, is this an omission from Spring security OAuth2? If so, guess a bug should be raised.
I could replace the BasicAuthenticationFilter, with one that has the URL Decode step, but is there an easier way?
I am currently using Spring Security 5.0.5
Spring security does comply with OAuth 2.0 spec. What you're asking is already covered in BasicAuthenticationFilter. Here's an excerpt from the code:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
boolean debug = this.logger.isDebugEnabled();
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Basic ")) {
try {
String[] tokens = this.extractAndDecodeHeader(header, request);
assert tokens.length == 2;
Filter reads the header that has Basic authentication, then it extracts and decode the credentials.
In the following code, notice that index of colon is used as a String delimiter.
private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException {
byte[] base64Token = header.substring(6).getBytes("UTF-8");
byte[] decoded;
try {
decoded = Base64.getDecoder().decode(base64Token);
} catch (IllegalArgumentException var7) {
throw new BadCredentialsException("Failed to decode basic authentication token");
}
String token = new String(decoded, this.getCredentialsCharset(request));
int delim = token.indexOf(":");
if (delim == -1) {
throw new BadCredentialsException("Invalid basic authentication token");
} else {
return new String[]{token.substring(0, delim), token.substring(delim + 1)};
}
}

Apache Camel - from jms to http

I have a spring-boot project using Apache Camel.
I want to read a message from an activemq queue containing a file and send it to a web server.
I am trying to find the proper way to do this.
I believe I can make something like:
from("activemq:queue").bean(MyBean.class, "process")
And manually build a http request but I can't help thinking there is probably a better way to do it. Like:
from("activemq:queue").bean(MyBean.class, "process")
.setHeader(Exchange.HTTP_METHOD,constant("POST"))
.to("http://localhost:8080/test");
But I don't know how to manipulate the "exchange" to have a valid http Message.
MyBean receives an Exchange object containing a JmsMessage. I see that there is also a HTTPMessage but I don't think I should build that manually. (It requires HTTPRequest and Response objects I am not sure how to get.)
Can someone shed some light on this problem?
Update
I am going for the bean solution.
from("activemq:queue").bean(MyBean.class, "sendMultipart");
public void sendMultipart(Exchange exchange) {
ByteArrayInputStream in = new ByteArrayInputStream((byte[]) exchange.getIn().getBody());
InputStreamBody contentBody = new InputStreamBody(in, ContentType.create("application/octet-stream"), "filename");
HttpEntity entity = MultipartEntityBuilder
.create()
.addPart("file", contentBody)
.build();
HttpPost httpPost = new HttpPost("http://localhost:8080/upload/");
httpPost.setEntity(entity);
CloseableHttpClient httpClient = HttpClients.createDefault();
try {
CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
System.out.println(httpResponse);
} catch (IOException e) {
e.printStackTrace();
}
}
Updated post
I found this http://hilton.org.uk/blog/camel-multipart-form-data. It allows you to leverage the camel http component.
"jms:queue/SomeQ" ==> {
process(toMultipart)
setHeader(Exchange.CONTENT_TYPE, "multipart/form-data")
process((e: Exchange) => e.getIn.setHeader(Exchange.HTTP_URI,"http://localhost:8111/foo"))
to ("http:DUMMY")
}
def toMultipart(exchange: Exchange): Unit = {
val data = exchange.in[java.io.File]
val entity = MultipartEntityBuilder.create()
entity.addBinaryBody("file", data)
entity.addTextBody("name", "sample-data")
// Set multipart entity as the outgoing message’s body…
exchange.in = entity.build
}
Side note: this would really be a nice use-case to try-out reactive streams.
Original post
I am still having some problems understanding your actual problem. Perhaps some code might help:
I am now assuming you are receiving bytes in some character encoding and want to sent it onward to a dynamically established http-endpoint.
Is the following something you are looking for (code is in camel's scala-dsl)
"jms:queue/SomeQ" ==> {
convertBodyTo(classOf[String],"UTF-32" )
process((e: Exchange) => e.in = e.in[String].toUpperCase + "!")
process((e: Exchange) => e.getIn.setHeader(Exchange.HTTP_URI,"http://localhost:8111/foo"))
to ("http:DUMMY")
}
It will be send as an HTTP POST as the body is not null.
I receive it all well on another endpoint i created to ensure the code above is correct:
"jetty:http://localhost:8111/foo" ==> {
log("received on http 8111 endpoint ${body}")
}

Best practice for deploying spring boot application on Amazon

I've devloped a chat bot application using the Facebook Messenger platform.
I used Spring Boot with embedded Tomcat for the web platform.
The application should run on Amazon aws, open to the WWW, and to be used as a webhook for recieving callbacks from Messenger over https.
I need an advice how to secure the application, so it won't be hacked or flooded with requests that are not coming from Facebook.
I thought to make the application require secured (ssl) connection, but using the "security.require_ssl=true" in application.properties didn't do the work. Perhaps I don't know what is the meaning of this and how to configure it propertly.
Is there a best practice how to block requests which are not https requests? Or a way to block requests which are coming outside Messenger in the application level?
Thank you very much!
EDIT
In the meantime, I blocked requests from other IPs in application layer using the handler interceptor:
#Configuration
public class MyWebApplicationInitializer implements WebApplicationInitializer, WebMvcConfigurer{
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptor() {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (! (request.getRemoteAddr().equals("173.252.88.66") || request.getRemoteAddr().equals("127.0.0.1")|| request.getRemoteAddr().equals("0:0:0:0:0:0:0:1"))){
logger.warn("Request is not coming from authorized remote address: " + request.getRemoteAddr()+". Rejecting");
response.getWriter().write("Unauthorized Address");
response.setStatus(401);
return false;
} else {
return true;
}
}
}
You should check the X-Hub-signature HTTP header available in the requests sent by Facebook to your webhook URL.
In your case, you may define a filter or interceptor for the verification of the signature. You can also do it in your controller as in the this example I found in RealTimeUpdateController.java from the spring social project.
private boolean verifySignature(String payload, String signature) throws Exception {
if (!signature.startsWith("sha1=")) {
return false;
}
String expected = signature.substring(5);
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
SecretKeySpec signingKey = new SecretKeySpec(applicationSecret.getBytes(), HMAC_SHA1_ALGORITHM);
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(payload.getBytes());
String actual = new String(Hex.encode(rawHmac));
return expected.equals(actual);
}
a lot to say so I am sure I will miss some points.
setting SSL is a first good thing but make sure you get a certificate. lets encrypt is a good thing if you dont want to pay for SSL certificate.
Just seeing aws provides an alternative to letsencrypt
Security Group You can see Security Group as something similar to a firewall so you can control which port is opened, external and internal flows.
Look at IAM which control who and how can get access to your AWS account
obvious : change your password. do not let default password for installation you could make on the instance
read some of https://aws.amazon.com/security/security-resources/ to get more information about what you can do
it won't be hacked or flooded with requests
sorry to say but most probably it will be - It does not need to be an advanced hacker to run scanner and scan IPs and check open ports / brute force login etc ...
Thanks to Guy Bouallet help I added the signature check.
I added it in my controller and not in the interceptor, to avoid the problem of How to read data twice in spring which seems a little complicated.
So here is it:
#RequestMapping(path = "/")
public void doHandleCallback(#RequestBody String body, #RequestHeader(value = "X-Hub-Signature") String signature) throws IOException {
if (!verifyRequestSignature(body.getBytes(), signature)){
logger.error ("Signature mismatch.");
throw new MismatchSignatureException(signature);
}
MessengerCallback callback = mapper.readValue(body, MessengerCallback.class);
logger.info("Incoming Callback: " + body );
for (EventData entry : callback.getEntry()) {
for (ReceivedMessagingObject message : entry.getMessaging()) {
if (message.isMessage() || message.isPostback()) {
doHandleMessage(message);
}
else if (message.isDelivery()){
doHandleDelivery(message);
}
}
}
}
private boolean verifyRequestSignature(byte[] payload, String signature) {
if (!signature.startsWith("sha1="))
return false;
String expected = signature.substring(5);
System.out.println("Expected signature: " + expected); //for debugging purposes
String hashResult = HmacUtils.hmacSha1Hex(APP_SECRET.getBytes(), payload);
System.out.println("Calculated signature: " + hashResult);
if (hashResult.equals(expected)) {
return true;
} else {
return false;
}
}
And this is the Exception handling class:
#ResponseStatus(value=HttpStatus.BAD_REQUEST, reason="Request Signature mismatch")
public class MismatchSignatureException extends RuntimeException {
private String signature;
public MismatchSignatureException(String signature) {
this.signature = signature;
}
#Override
public String getMessage() {
return "Signature mismatch: " + signature;
}

Basic Authentication with Resteasy client

I'm trying to perform an basic auth to the login-module which runs on my jboss using REST. I already found an StackOverflow topic which explains how to authenticate with credentials.
RESTEasy client framework authentication credentials
This does not work. Analysing the established connection with Wireshark I was not able to see an HTTP package with Authorization: Basic. After more research I found this article, http://docs.jboss.org/resteasy/docs/2.3.3.Final/userguide/html/RESTEasy_Client_Framework.html which describes how to append basic auth to ApacheHttpClient4Executor from resteasy.
// Configure HttpClient to authenticate preemptively
// by prepopulating the authentication data cache.
// 1. Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// 2. Generate BASIC scheme object and add it to the local auth cache
BasicScheme basicAuth = new BasicScheme();
authCache.put("com.bluemonkeydiamond.sippycups", basicAuth);
// 3. Add AuthCache to the execution context
BasicHttpContext localContext = new BasicHttpContext();
localContext.setAttribute(ClientContext.AUTH_CACHE, authCache);
// 4. Create client executor and proxy
httpClient = new DefaultHttpClient();
ApacheHttpClient4Executor executor = new ApacheHttpClient4Executor(httpClient, localContext);
client = ProxyFactory.create(BookStoreService.class, url, executor);
But this does not work either. There is no description how to append username and passwort for basic auth to the construct. Why is that information not associated with any class from httpcomponent?
One can use org.jboss.resteasy.client.jaxrs.BasicAuthentication which is packaged with resteasy-client 3.x and is meant specifically for basic authentication.
Client client = ClientBuilder.newClient();
ResteasyWebTarget resteasyWebTarget = (ResteasyWebTarget)client.target("http://mywebservice/rest/api");
resteasyWebTarget.register(new BasicAuthentication("username", "passwd"));
You can add a raw authorization header to your REST client by invoking .header(HttpHeaders.AUTHORIZATION, authHeader) in your client configuration.
The credentials must be packed in authorization header in the format of "user:pass", encoded as base64 byte array and then appended to the string "Basic " which identifies basic auth.
This is the whole snippet (inspired by this post on baeldung)
String auth = userName + ":" + password;
byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(Charset.forName("ISO-8859-1")));
String authHeader = "Basic " + new String(encodedAuth);
authToken = restClient.target(restApiUrl + loginPath)
.request()
.accept(MediaType.TEXT_PLAIN)
.header(HttpHeaders.AUTHORIZATION, authHeader)
.get(String.class);
This worked for me in a Resteasy client. For information, when testing this with wget I had to use the --auth-no-challenge flag.
Consider the solution from Adam Bien:
You can attach an ClientRequestFilter to the RESTEasy Client, which adds the Authorization header to the request:
public class Authenticator implements ClientRequestFilter {
private final String user;
private final String password;
public Authenticator(String user, String password) {
this.user = user;
this.password = password;
}
public void filter(ClientRequestContext requestContext) throws IOException {
MultivaluedMap<String, Object> headers = requestContext.getHeaders();
final String basicAuthentication = getBasicAuthentication();
headers.add("Authorization", basicAuthentication);
}
private String getBasicAuthentication() {
String token = this.user + ":" + this.password;
try {
return "Basic " +
DatatypeConverter.printBase64Binary(token.getBytes("UTF-8"));
} catch (UnsupportedEncodingException ex) {
throw new IllegalStateException("Cannot encode with UTF-8", ex);
}
}
}
Client client = ClientBuilder.newClient()
.register(new Authenticator(user, password));
I recently upgraded to resteasy-client:4.0.0.Final to deal with some Jackson upgrade issues, and I noticed that setting headers seem to work differently (I was getting 401: Authorization Errors for every authenticated request that previously worked). I also couldn't find much documentation, (the 4.0.0.Final release is only a month old and has some dependency issues, if my experience is representative of the broader case).
The code previously injected headers into the ClientRequestContext:
public AddAuthHeadersRequestFilter(String username, String password) {
this.username = username;
this.password = password;
}
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
String token = username + ":" + password;
String base64Token = Base64.encodeString(token);
requestContext.getHeaders().add("Authorization", "Basic " + base64Token);
}
}
then we set the filter on the ResteasyClient like so:
ResteasyClient client = new ResteasyClientBuilder()
.sslContext(buildSSLContext())
.hostnameVerifier(buildHostVerifier())
.build();
client.register(new AddAuthHeadersRequestFilter(user, pass));
However, this appears not to set the HeaderDelegate, which is where headers are retrieved in 4.x(?) and possibly earlier versions.
The trick was to register that filter on the ResteasyWebTarget instead of the client in the 4.0.0.Final version (you may notice the clientBuilder works a little differently now too).
ResteasyClient client = (ResteasyClient)ResteasyClientBuilder.newBuilder()
.sslContext(buildSSLContext())
.hostnameVerifier(buildHostVerifier())
.build();
ResteasyWebTarget target = client.target(url);
target.register(new AddAuthHeadersRequestFilter(user, pass));

Resources