I m training my self on creating a restful server and a desktop java based client.
my backend is Spring Boot based, I have the following controller :
#RestController
#RequestMapping(NiveauAccessController.URL)
public class NiveauAccessController extends GenericController{
public static final String URL = "/acl";
#Autowired
private NiveauAccessRepository niveauAccessRepository;
#PostMapping
private ServerResponse createACL(
#RequestParam("aclTitle") final String aclTitle,
#RequestParam("roles") final List<String> roles
){
if(isSessionValid()){
final MNG_NIVEAU_ACCEE mng_niveau_accee = new MNG_NIVEAU_ACCEE();
mng_niveau_accee.setAclTitle(aclTitle);
List<Role> enumRoles = new ArrayList();
roles.stream().forEach(role->{
enumRoles.add(Role.valueOf(role));
});
mng_niveau_accee.setRoles(enumRoles);
niveauAccessRepository.save(mng_niveau_accee);
initSuccessResponse(mng_niveau_accee);
return serverResponse;
}
initFailLoginResponse();
return serverResponse;
}
.
.
.
}
for my java client I m using this sample code to send a post request over my server :
#FXML
private void doAdd(ActionEvent event) throws UnirestException {
if (titleACL.getText().isEmpty()) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.initModality(Modality.WINDOW_MODAL);
alert.initOwner(((Node) event.getSource()).getScene().getWindow());
alert.setContentText("Veuillez remplir le champ");
alert.showAndWait();
titleACL.requestFocus();
return;
}
String title = titleACL.getText();
Predicate<? super JFXCheckBox> selectedCheckboxes = checkbox -> {
return checkbox.isSelected();
};
List<JFXCheckBox> selectedCheckBoxesList = observableCheckBoxes.values().stream().filter(selectedCheckboxes).collect(Collectors.toList());
final List<String> roles = new ArrayList<>();
selectedCheckBoxesList.stream().forEach(checkbox -> {
roles.add(checkbox.getText());
});
HttpResponse<String> asString = Unirest.post(ACL_URL)
.header("accept", "application/json")
.field("aclTitle", title)
.field("roles", roles)
.asString();
System.out.println(asString.getStatus());
System.out.println(asString.getHeaders().values());
if (asString.getStatus() == 200) {
}
}
my output is :
302
[[0], [Thu, 10 May 2018 13:30:05 GMT], [https://localhost:8443/acl]]
I don't understand why I m getting the 302 status code which is for URL redirection.
I m trying to use this post to add data to my database.
What should I do to make my Server accept this request?
havins ssl enabled my request over 8080 got redirection to 8443 this is no issue using a web browser because it will handle redirection but in a javafx client you have to handle the redirect by your self so there is a possible solution
if (asString.getStatus() == 200) {
//your success handler code
}else if (asString.getStatus() == 302) {
// use your Rest api to do the request using the response body
}
Related
I use Retrofit2 to make REST API requests. I have my dummy server (that runs with spring boot) on my machine:
#RestController
class SecureServiceController {
private int counter = 1;
#RequestMapping(value = "/nnrf-nfm/v1/nf-instances/bee75393-2ac3-4e60-9503-854e733309d4", method = RequestMethod.PUT)
public ResponseEntity<NFProfile> nNrfNfManagementNfRegister() {
System.out.println(counter++ + ". Got NrfClient register request. " + new Date());
NFProfile nfProfile = new NFProfile();
nfProfile.setHeartBeatTimer(2);
ResponseEntity<NFProfile> responseEntity = ResponseEntity.status(201).body(nfProfile);
return responseEntity;
}
}
When client make request from the same machine it works. But when client make request from remote machine I have error response:
Response{protocol=http/1.1, code=401, message=Unauthorized, url=https://myhostname:8443/nnrf-nfm/v1/nf-instances/bee75393-2ac3-4e60-9503-854e733309d4}
Response error body: <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"><html><head><title>Error</title></head><body><h1>Error</h1></body></html>
I've read that such error means that client don't have the rights to access and need to add access token. But my server does not ask any access token (at least explicitly) and it should not ask it.
How to solve this problem?
My apiClient:
public class ApiClient {
private Map<String, Interceptor> apiAuthorizations;
private Builder okBuilder;
private retrofit2.Retrofit.Builder adapterBuilder;
private JSON json;
//a lot setters and getters
public <S> S createService(Class<S> serviceClass) {
return this.adapterBuilder.client(this.okBuilder.build()).build().create(serviceClass);
}
public void configureFromOkclient(OkHttpClient okClient) {
this.okBuilder = okClient.newBuilder();
this.addAuthsToOkBuilder(this.okBuilder);
}
}
my interface:
public interface NfInstanceIdDocumentApi {
#Headers({"Content-Type:application/json"})
#PUT("nf-instances/{nfInstanceID}")
Call<NFProfile> registerNFInstance(#Body NFProfile body, #Path("nfInstanceID") UUID nfInstanceID, #Header("Content-Encoding") String contentEncoding, #Header("Accept-Encoding") String acceptEncoding);
}
How I do call:
OkHttpClient okHttpClient= ClientFactory.createClient();
ApiClient client = new ApiClient();
client.configureFromOkclient(okHttpClient);
NFProfile body = getNfProfile();
String baseUri = getBaseUri();
UUID uuid = getUUID();
//create call
client.getAdapterBuilder().baseUrl(baseUri);
NfInstanceIdDocumentApi service = client.createService(NfInstanceIdDocumentApi.class);
Call<NFProfile> call = service.registerNFInstance(body, uuid, null, null);
//make call
Response<NFProfile> response = call.execute();
UPD
I found the problem. Server was running on Windows machine and firewall blocked incoming requests.
I have to call one secured endpoint from rest client and at the controller side it require the authorities and user principal information to be sent from client.
String endpoint="http://localhost:8096/polygons/34";
// endpoint="https://dop-int.edosdp.ericsson.se/polygon-manager/polygons/34";
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth("mahi", "ChangeM6");
headers.setConnection("keep-alive");
HttpEntity<String> httpEntity = new HttpEntity<String>(headers);
ResponseEntity<Long> exchange = restTemplate.exchange(endpoint,HttpMethod.GET, httpEntity, Long.class);
how can send at least one role(ADMIN or GUEST_USER) information from client .
IS there any way I can wrap up all user info in a dummy session and send it to the serer.
Thanks ,
Mahi
No! It's a bad idea for the client to modify any kind of session information including cookies. Only the server should be allowed to do that.
Since your requirement is to check for user role on a specific url, you can set a custom request header and check for it within the controller method itself:
Example code:
#GetMapping("/polygons")
public String getPolygons(HttpServletRequest request) {
String userRole = request.getHeader("user-role");
if(userRole != null && userRole.toLowerCase().equals("admin")) {
System.out.print("Role provided: " + userRole);
// ...
return "some-data";
}
System.out.print("Role not provided!");
return "error";
}
You could also set the user role in the request body for a post request.
public class RequestParams {
private String userRole;
// ...
}
#PostMapping("/polygons")
public String getPolygons(#RequestBody RequestParams requestParams) {
String userRole = requestParams.getUserRole();
if(userRole != null && userRole.toLowerCase().equals("admin")) {
System.out.print("Role provided: " + userRole);
// ...
return "some-data";
}
System.out.print("Role not provided!");
return "error";
}
If your requirement is to check for the user role on multiple urls then you should consider writing a servlet filter.
EDIT:
I think I too faced a similar situation in the past. I ended up using apache's httpclient library instead of resttemplate.
Here's some sample code:
private List<OrganizationDTO> getUserOrgUnits(String loggedInUserId, String token) {
List<OrganizationDTO> userList = new ArrayList<OrganizationDTO>();
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(getUserOrgUnitsApiURL());
try {
// Setting header
httpGet.setHeader("Authorization", "Bearer " + token);
httpGet.setHeader("Content-type", "application/json");
// Setting custom header
httpGet.setHeader(USERID_HEADER_NAME, loggedInUserId);
CloseableHttpResponse response = httpClient.execute(httpGet);
String result = EntityUtils.toString(response.getEntity());
JsonNode node = null;
ObjectMapper mapper = new ObjectMapper();
node = mapper.readTree(result);
Iterable<JsonNode> list = node.path("data");
for (JsonNode jsonNode : list) {
OrganizationDTO dto = mapper.treeToValue(jsonNode, OrganizationDTO.class);
userList.add(dto);
}
} catch (Exception e) {
log.error("getUserOrgUnits: Exception.");
e.printStackTrace();
}
return userList;
}
I need to capture public ip of the system calling our api, that system is a desktop Application thats calling our rest api and I am using the following code
#RequestMapping(value = "/api", method = RequestMethod.POST)
public #ResponseBody JSON_Response sendMessage(#RequestBody Objectjson objjson,HttpServletRequest
request) {
LOG.debug("sending message via payless server");
inputjson.setSentFrom("Message Sent From Api");
// inputjson.setHostip(""+request.getRemoteHost());
//I am using following to capture it
LOG.debug("--------------------------"+request.getRemoteUser());
LOG.debug("--------------------------"+request.getLocalAddr());
LOG.debug("--------------------------"+request.getRemoteHost());
LOG.debug("--------------------------"+request.getPathInfo());
LOG.debug("--------------------------"+request.getPathTranslated());
LOG.debug("--------------------------"+request.getRemoteUser());
LOG.debug("--------------------------"+request.getRemoteUser());
LOG.debug("--------------------------"+request.getRemoteUser());
LOG.debug("--------------------------"+request.getRemoteUser());
LOG.debug("--------------------------"+request.getRemoteUser());
JSON_Response response = sendMessageInterface.processInput(inputjson);
LOG.debug("sending response message " + response.getStatusDescription());
return response;
}
I am getting my own server ip in the ip address.If i call the rest api from postman i am getting the correct ip address.
Please let me know if you find any other way to retrieve public ip .
I am using Wildfly Server wildfly-10.1.0.Final
This is the method that i Use to get the remote user IP address. Hope it helps
public HttpServletRequest getRequest() {
RequestAttributes attribs = RequestContextHolder.getRequestAttributes();
if (attribs instanceof ServletRequestAttributes) {
HttpServletRequest request = ((ServletRequestAttributes)attribs).getRequest();
return request;
}
return null;
}
public String getClientIp() {
String remoteAddr = "";
HttpServletRequest request = getRequest();
if (request != null) {
remoteAddr = request.getHeader("X-FORWARDED-FOR");
if (remoteAddr == null || remoteAddr.trim().isEmpty()) {
remoteAddr = request.getRemoteAddr();
}
}
return remoteAddr;
}
I'm having some issues running a web app which is basically a URL shortener.
The server have a functionality that allows to upload a CSV file with a list of URLs to short. The code bellow is a method that takes a CSV file from a queue, it reads the file and shorts the URLs in it. The problem comes when I try to send a post request to on of the controllers in my server. The exception that appears is the following:
javax.ws.rs.ProcessingException: HTTP 500 Internal Server Error
Here is the code of the method I mentioned:
while(true){
QueueObject qo = csvQueue.take();
copyFile(qo.getFile());
File f = new File("temp");
Scanner sc = new Scanner(f);
sc.useDelimiter(",|\\s");
Client client = ClientBuilder.newClient();
while(sc.hasNext()){
String url = sc.next();
ResponseEntity<ShortURL> res = shortener(url, null, null, null, null, null);
if(res!=null && ((res.getStatusCode()).toString()).equals("400")){
String stat = url + " : Failed";
UpdateMessage um = new UpdateMessage(stat, qo.getUser());
Response response = client.target("http://localhost:8080/urlUploads")
.request(MediaType.APPLICATION_JSON)
.post(Entity.entity(um, MediaType.APPLICATION_JSON));
}
else{
String stat = url + " : Success";
UpdateMessage um = new UpdateMessage(stat, qo.getUser());
Response response = client.target("http://localhost:8080/urlUploads")
.request(MediaType.APPLICATION_JSON)
.post(Entity.entity(um, MediaType.APPLICATION_JSON));
}
}
f.delete();
}
As I said, the problem is on this specific request (both are basically the same):
Response response = client.target("http://localhost:8080/urlUploads")
.request(MediaType.APPLICATION_JSON)
.post(Entity.entity(um, MediaType.APPLICATION_JSON));
The controller I'm trying to reach is this one:
#Controller
public class WebSocketController {
private SimpMessagingTemplate template;
private static final Logger logger = LoggerFactory.getLogger(WebSocketController.class);
#Autowired
public WebSocketController(SimpMessagingTemplate template) {
this.template = template;
}
#RequestMapping(value="/urlUploads", method=RequestMethod.POST)
public void greet(UpdateMessage update) {
this.template.convertAndSendToUser(update.getUser(), "/sockets/urlUploads", update.getStatus());
}
}
I am trying to use Bonita Web API. I My code is below. As you can see I call the loginservice before calling any other API service. It logs in OK 200. But when I make the subsequent call to get the list of processes I get a 401 error. You get a JSESSIONID from the first call and you are suppose to pass it to the subsequent calls to authenticate you.
var baseAddress = new Uri(<base address>);
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
HttpResponseMessage result = client.PostAsync("/bonita/loginservice", new StringContent("login=<username>,password=<password>,redirect=false")).Result;
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage result2 = client.GetAsync("/bonita/API/bpm/process").Result;
result2.EnsureSuccessStatusCode();
}
This works for .Net 2.0 C# but has some interesting things to check.
WebClient wc = new WebClient();
wc.Proxy = WebRequest.GetSystemWebProxy();
//wc.Headers[HttpRequestHeader.AcceptEncoding] = "gzip, deflate";
string strLogin = wc.DownloadString("http://localhost:8080/bonita/loginservice?username=walter.bates&password=bpm&redirect=false");
wc.Headers[HttpRequestHeader.Cookie] = wc.ResponseHeaders[HttpResponseHeader.SetCookie].ToString();
string strCookie = wc.ResponseHeaders[HttpResponseHeader.SetCookie].ToString();
string strProcesses = wc.DownloadString("http://localhost:8080/bonita/API/bpm/process?p=0");
First of all you should know how to determine that the executed operation is successful ( login, getProcesses and whatever) When you try to login you will always get the header (for example "JSESSIONID=50E509D37AC28E2D725CBD45A8112FA7; Path=/bonita; HttpOnly") and OK 200 even if your login attempt in Bonita is unsuccesful.
For the successful login on the previous example
1) You must Pass mandatory form data: username, password and redirect You must also be sure to pass redirect in lower case ."False" will not work, "false" will work. So for .Net suppose you have a property-> Boolean redirect. You must make it lowercase with redirect.ToString().ToLower() cause either way the value will be "False" and you don't want that.
Let's say you try to login only with username and password without passing redirect. the result is that you will get both OK 200 and the header but you will also get a response which is wrong (the response must be empty), so on the next request (i.e getProcesses) you'll get (401) Unauthorized. Guess the results you will have if you pass redirect=False instead of redirect=false. Exactly the same.
2)You must get: strLogin="" // the body of the response must be empty strCookie="JSESSIONID=4F67F134840A2C72DBB968D53772FB22; Path=/bonita; HttpOnly"
For the successful getProcesses on the previous example you pass the header you got from login
wc.Headers[HttpRequestHeader.Cookie] = wc.ResponseHeaders[HttpResponseHeader.SetCookie].ToString();
and then you call the process and get a string in json format for example
"[{\"id\":\"6996906669894804403\",\"icon\":\"\",\"displayDescription\":\"\",\"deploymentDate\":\"2014-11-19 17:57:40.893\",\"description\":\"\",\"activationState\":\"ENABLED\",\"name\":\"Travel request\",\"deployedBy\":\"22\",\"displayName\":\"Travel request\",\"actorinitiatorid\":\"4\",\"last_update_date\":\"2014-11-19 17:57:41.753\",\"configurationState\":\"RESOLVED\",\"version\":\"1.0\"}]"
(or [] which means an empty json)
If the cookie is not passed correctly you will get again 401 error.
Solution for .Net 4.5.1
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace BonitaRestApi
{
class BonitaApi
{
private CookieCollection collection;
string strCookietoPass;
string sessionID;
static void Main(string[] args)
{
BonitaApi obj = new BonitaApi();
Task login = new Task(obj.Login);
login.Start();
login.Wait();
Console.ReadLine();
Task GetProcesses = new Task(obj.GetProcesses);
GetProcesses.Start();
GetProcesses.Wait();
Console.ReadLine();
Task logout = new Task(obj.Logout);
logout.Start();
logout.Wait();
Console.ReadLine();
}
public async void Login()
{
const string url = "http://localhost:8080/bonita/";
var cookies = new CookieContainer();
var handler = new HttpClientHandler();
handler.CookieContainer = cookies;
using (var client = new HttpClient(handler))
{
var uri = new Uri(url);
client.BaseAddress = uri;
//client.DefaultRequestHeaders.Accept.Clear();
//client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("username", "helen.kelly"),
new KeyValuePair<string, string>("password", "bpm"),
new KeyValuePair<string, string>("redirect", "false"),
new KeyValuePair<string, string>("redirectUrl", ""),
});
HttpResponseMessage response = await client.PostAsync("loginservice", content);
if (response.IsSuccessStatusCode)
{
var responseBodyAsText = await response.Content.ReadAsStringAsync();
if (!String.IsNullOrEmpty(responseBodyAsText))
{
Console.WriteLine("Unsuccessful Login.Bonita bundle may not have been started, or the URL is invalid.");
return;
}
collection= cookies.GetCookies(uri);
strCookietoPass = response.Headers.GetValues("Set-Cookie").FirstOrDefault();
sessionID = collection["JSESSIONID"].ToString();
Console.WriteLine(string.Format("Successful Login Retrieved session ID {0}", sessionID));
// Do useful work
}
else
{
Console.WriteLine("Login Error" + (int)response.StatusCode + "," + response.ReasonPhrase);
}
}
}
public async void Logout()
{
const string url = "http://localhost:8080/bonita/";
var cookies = new CookieContainer();
var handler = new HttpClientHandler();
handler.CookieContainer = cookies;
using (var client = new HttpClient(handler))
{
var uri = new Uri(url);
client.BaseAddress = uri;
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("redirect", "false")
});
HttpResponseMessage response = await client.PostAsync("logoutservice", content);
if (response.IsSuccessStatusCode)
{
var responseBodyText = await response.Content.ReadAsStringAsync();
if (!String.IsNullOrEmpty(responseBodyText))
{
Console.WriteLine("Unsuccessful Logout.Bonita bundle may not have been started, or the URL is invalid.");
return;
}
Console.WriteLine("Successfully Logged out.");
}
else
{
Console.WriteLine("Logout Error" + (int)response.StatusCode + "," + response.ReasonPhrase);
}
}
}
public async void GetProcesses()
{
var handler = new HttpClientHandler();
Cookie ok = new Cookie("Set-Cookie:",strCookietoPass);
handler.CookieContainer.Add(collection);
using (var client = new HttpClient(handler))
{
var builder = new UriBuilder("http://localhost/bonita/API/bpm/process");
builder.Port = 8080;
var query = HttpUtility.ParseQueryString(builder.Query);
query["p"] = "0";
query["c"] = "10";
builder.Query = query.ToString();
Uri uri= new Uri(builder.ToString());
client.BaseAddress = uri;
HttpResponseMessage response = await client.GetAsync(uri.ToString());
if (response.IsSuccessStatusCode)
{
var responseBodyText = await response.Content.ReadAsStringAsync();
if (String.IsNullOrEmpty(responseBodyText))
{
Console.WriteLine("Unsuccessful GetProcesses.Bonita bundle may not have been started, or the URL is invalid.");
return;
}
Console.WriteLine("Successfully GetProcesses:" + responseBodyText);
}
else
{
Console.WriteLine("GetProcesses Error" + (int)response.StatusCode + "," + response.ReasonPhrase);
}
}
}
}
}
I had the same problem (401 errors) for every single non-GET request.
I finally got through this by looking to the CSRF documentation:
http://documentation.bonitasoft.com/7.4?page=csrf-security
(See the "Is there an impact on REST API calls?" section)
After succesfull login, you have to put a special header in your request:
key: X-Bonita-API-Token
value: the one you got after your login (check the relevant cookie)