I'm trying to implement an authentication on my current Java Restful API (Jersey) by following this tutorial. I put all new authentication classes inside a "auth" package. When I run the jquery example code, it says that "DemoBusinessRESTResourceProxy is an interface and cannot be instantiated". So I research and decided to put the Jersey annotations on DemoBusinessRESTResource and delete DemoBusinessRESTResourceProxy:
#Local
#Path("access/")
#Stateless(name = "DemoBusinessRESTResource", mappedName = "ejb/DemoBusinessRESTResource")
public class DemoBusinessRESTResource {
private static final long serialVersionUID = -6663599014192066936L;
#POST
#Path("/login")
#Produces(MediaType.APPLICATION_JSON)
public Response login(#Context HttpHeaders httpHeaders,
#FormParam("username") String username,
#FormParam("password") String password) {
Authenticator authenticator = Authenticator.getInstance();
String serviceKey = httpHeaders
.getHeaderString(HTTPHeaderNames.SERVICE_KEY);
try {
String authToken = authenticator.login(serviceKey, username,
password);
JsonObjectBuilder jsonObjBuilder = Json.createObjectBuilder();
jsonObjBuilder.add("auth_token", authToken);
JsonObject jsonObj = jsonObjBuilder.build();
return getNoCacheResponseBuilder(Response.Status.OK).entity(
jsonObj.toString()).build();
} catch (final LoginException ex) {
JsonObjectBuilder jsonObjBuilder = Json.createObjectBuilder();
jsonObjBuilder.add("message",
"Problem matching service key, username and password");
JsonObject jsonObj = jsonObjBuilder.build();
return getNoCacheResponseBuilder(Response.Status.UNAUTHORIZED)
.entity(jsonObj.toString()).build();
}
}
#GET
#Path("/demo-get-method")
#Produces(MediaType.APPLICATION_JSON)
public Response demoGetMethod() {
JsonObjectBuilder jsonObjBuilder = Json.createObjectBuilder();
jsonObjBuilder.add("message", "Executed demoGetMethod");
JsonObject jsonObj = jsonObjBuilder.build();
return getNoCacheResponseBuilder(Response.Status.OK).entity(
jsonObj.toString()).build();
}
#POST
#Path("/demo-post-method")
#Produces(MediaType.APPLICATION_JSON)
public Response demoPostMethod() {
JsonObjectBuilder jsonObjBuilder = Json.createObjectBuilder();
jsonObjBuilder.add("message", "Executed demoPostMethod");
JsonObject jsonObj = jsonObjBuilder.build();
return getNoCacheResponseBuilder(Response.Status.ACCEPTED).entity(
jsonObj.toString()).build();
}
#POST
#Path("/logout")
public Response logout(#Context HttpHeaders httpHeaders) {
try {
Authenticator authenticator = Authenticator.getInstance();
String serviceKey = httpHeaders
.getHeaderString(HTTPHeaderNames.SERVICE_KEY);
String authToken = httpHeaders
.getHeaderString(HTTPHeaderNames.AUTH_TOKEN);
authenticator.logout(serviceKey, authToken);
return getNoCacheResponseBuilder(Response.Status.NO_CONTENT)
.build();
} catch (final GeneralSecurityException ex) {
return getNoCacheResponseBuilder(
Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
private Response.ResponseBuilder getNoCacheResponseBuilder(
Response.Status status) {
CacheControl cc = new CacheControl();
cc.setNoCache(true);
cc.setMaxAge(-1);
cc.setMustRevalidate(true);
return Response.status(status).cacheControl(cc);
}
}
Now, I'm getting this error:
A system exception occurred during an invocation on EJB BusinessRESTResource, method: public javax.ws.rs.core.Response com.rest.auth.DemoBusinessRESTResource.login(javax.ws.rs.core.HttpHeaders,java.lang.String,java.lang.String), Caused by: java.lang.AbstractMethodError: com.sun.jersey.spi.container.ContainerRequest.getHeaderString(Ljava/lang/String;)Ljava/lang/String;
I'm new in Java WS and I'm totally lost.
I found the problem. The problem was the jar version files.
Related
i have created RequestBody with Gson and add to Post request, but in the controller of api (url), all attrs return null.
Add to RequestBody:
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
Gson gson = new Gson();
JSONObject jsonObject = new JSONObject();
jsonObject.put("dataFromAppChinh", "demo thoi");
jsonObject.put("path", path);
String body = gson.toJson(jsonObject);
RequestBody requestBody = RequestBody.create(body, JSON);
Request request = new Request.Builder().url("http://" + path + "/api/startapp").post(requestBody).build();
Controller
#RestController
#RequestMapping("/api")
public class ChatBot {
#PostMapping(value = "/startapp")
#ResponseBody
public Object start(#RequestBody() ChatBotResponse item) {
try {
item.setResponseList(startApp(item.getPath()));
return item;
} catch (Exception ex) {
log.error(ex);
return ex.getMessage();
}
}
}
My ChatBotResponse POJO:
#Data
public class ChatBotResponse
{
private String dataFromAppChinh;
private String responseList;
private String path;
}
i found problem is gson.toJson(jsonObject) insert map string before my Json String.
I changed to jsonObject.toString() and it worked.
I am new to spring boot here. I have three micro-services one for user registration(authentication) and login(authorization) and two other services that requires authorization from the first.
Currently, the first microservice serves as the authentication and authorization point as no user can access the other two services without having passed through the first one.
Apparently, I retrieve the request header in the other two services to be able to get unique user details from the database and return unique resources.
#PostMapping("/clients")
public ResponseEntity<ResponseModel> addClient(#Valid #RequestBody Client client, #RequestHeader("Authorization") String auth) {
ClientRequest clientCreated = clientService.addClient(client, auth);
return handleSuccessResponseEntity("Client added successfully", HttpStatus.CREATED, clientCreated);
}
And also have authentication filter and authentication entry point to authenticate request in all the three services. I am not sure if this is a good implementation.
#Component
#Slf4j
public class JwtFilter extends OncePerRequestFilter {
#Autowired
private JWTUtility jwtUtility;
#Autowired
private ClientService clientService;
public static String token = null;
public static String userName = null;
public static Long userId = 0L;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String authorization = request.getHeader("Authorization");
try{
if (null != authorization && authorization.startsWith("Bearer ")) {
token = authorization.substring(7);
userName = jwtUtility.getEmailAddressFromToken(token);
userId = jwtUtility.getIdFromToken(token);
}
else{
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
ErrorResponse error = new ErrorResponse(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.toString(), "You are not authorized");
final ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.getOutputStream(), error);
}
}
catch (Exception e){
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
ErrorResponse error = new ErrorResponse(HttpStatus.FORBIDDEN.value(), HttpStatus.FORBIDDEN.toString(), e.getMessage());
final ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.getOutputStream(), error);
}
try{
if (0 != userId && null != userName && SecurityContextHolder.getContext().getAuthentication() == null) {
log.info("token "+token + "\n" + "userId " + userId);
UserDetails userDetails = clientService.getUserById(Long.valueOf(userId), token);
log.info("UserDTO {}", userDetails);
if (jwtUtility.validateTokenTwo(token)) {
log.info("token is valid");
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities()
);
usernamePasswordAuthenticationToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request)
);
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
filterChain.doFilter(request, response);
}
catch (HttpClientErrorException e){
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(e.getRawStatusCode());
ErrorResponse error = new ErrorResponse(e.getRawStatusCode(), String.valueOf(e.getRawStatusCode()), e.getStatusCode());
final ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.getOutputStream(), error);
}
}
}
#Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);
#Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
AuthenticationException e) throws IOException, ServletException {
logger.error("Responding with unauthorized error. Message - {}", e.getMessage());
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
ErrorResponse error = new ErrorResponse(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.toString(), "You are not authorized");
final ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(httpServletResponse.getOutputStream(), error);
}
}
However, What I would love to do if possible, is a dedicated service that will handle the auth and I can just decode the token from there without having to retrieve it from the header. In a nut-shell, to not have to retrieve token from the header.
Is this possible or what other way regards as standard can I take?
I'm new to the Java 11 HttpClient and would like to give it a try. I have a simple GET request that return JSON and I would like to map the JSON response to a Java class called Questionnaire.
I understand that I can turn the response out of box into a String or an input stream like this
HttpRequest request = HttpRequest.newBuilder(new URI(String.format("%s%s", this.baseURI, "/state")))
.header(ACCEPT, APPLICATION_JSON)
.PUT(noBody()).build();
HttpResponse<String> response = this.client.send(request, HttpResponse.BodyHandlers.ofString());
How can I write something that converts the JSON string to my Questionnaire class like this?
HttpResponse<Questionnaire> response = this.client.send(request, HttpResponse.BodyHandlers./* what can I do here? */);
I use Jackson to transform JSON into Java class instances. Is there Jackson support for the new Java standard HttpClient yet?
UPDATE 1
I was not precise enough, sorry about that. I am looking for a blocking get example. I was aware of http://openjdk.java.net/groups/net/httpclient/recipes.html#jsonGet
Solution for Java 11 HttpClient::sendAsync only
Based on this link you can do something like this :
public static void main(String[] args) throws IOException, URISyntaxException, ExecutionException, InterruptedException {
UncheckedObjectMapper uncheckedObjectMapper = new UncheckedObjectMapper();
HttpRequest request = HttpRequest.newBuilder(new URI("https://jsonplaceholder.typicode.com/todos/1"))
.header("Accept", "application/json")
.build();
Model model = HttpClient.newHttpClient()
.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenApply(uncheckedObjectMapper::readValue)
.get();
System.out.println(model);
}
class UncheckedObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper {
/**
* Parses the given JSON string into a Map.
*/
Model readValue(String content) {
try {
return this.readValue(content, new TypeReference<Model>() {
});
} catch (IOException ioe) {
throw new CompletionException(ioe);
}
}
}
class Model {
private String userId;
private String id;
private String title;
private boolean completed;
//getters setters constructors toString
}
I used some dummy endpoint which provides sample JSON input and sample model class to map the response directly to Model class using Jackson.
Solution for Java 11 HttpClient::send and HttpClient::sendAsync
I found a way by defining custom HttpResponse.BodyHandler :
public class JsonBodyHandler<W> implements HttpResponse.BodyHandler<W> {
private Class<W> wClass;
public JsonBodyHandler(Class<W> wClass) {
this.wClass = wClass;
}
#Override
public HttpResponse.BodySubscriber<W> apply(HttpResponse.ResponseInfo responseInfo) {
return asJSON(wClass);
}
public static <T> HttpResponse.BodySubscriber<T> asJSON(Class<T> targetType) {
HttpResponse.BodySubscriber<String> upstream = HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8);
return HttpResponse.BodySubscribers.mapping(
upstream,
(String body) -> {
try {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(body, targetType);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
}
}
Then I call it :
public static void main(String[] args) throws URISyntaxException, IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder(new URI("https://jsonplaceholder.typicode.com/todos/1"))
.header("Accept", "application/json")
.build();
Model model = HttpClient.newHttpClient()
.send(request, new JsonBodyHandler<>(Model.class))
.body();
System.out.println(model);
}
The response is :
Model{userId='1', id='1', title='delectus aut autem', completed=false}
The JavaDoc of HttpResponse.BodySubscribers::mapping was particulary useful to solve this. It can be further improved to use HttpResponse.BodySubscribers::ofInputStream instead of HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8) to define the BodySubscriber for the JsonBodyHandler.
Simplifying #michalk solution for Java 11 HttpClient::send
HttpService Class Example:
public class HttpService {
private final HttpClient httpClient= HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).build();
public HttpService() {}
public <T> T sendGetRequest(String url, Class<T> responseType) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder().GET().uri(URI.create(url)).header("Accept", "application/json").build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
return new ObjectMapper().readValue(response.body(), responseType);
}
public <T> List<T> sendGetListRequest(String url, Class<T> responseType) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder().GET().uri(URI.create(url)).header("Accept", "application/json").build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(response.body(), objectMapper.getTypeFactory().constructCollectionType(List.class, responseType));
}}
Model Class Example:
public class Model {
private String id;
public Model() {}
public String getId() { return this.id; }
public void setId(String id) { this.id = id; }
#Override
public String toString() { return "Model{" + "id='" + id + '\'' + '}'; }}
Sending HTTP GET request:
public class Main {
public static void main(String[] args) {
try {
HttpService httpService = new HttpService();
Model model = httpService.sendGetRequest("http://localhost:8080/api/v1/models/1", Model.class);
System.out.println("Single Object:" + model);
System.out.print('\n');
List<Model> models = httpService.sendGetListRequest("http://localhost:8080/api/v1/models", Model.class);
for(Model m: models) { System.out.println("Object:" + m); }
}
catch (IOException | InterruptedException e) {
System.err.println("Failed to send GET request: " + e.getMessage());
}
}}
Response:
Single Object: Model{id='1'}
Object: Model{id='1'}
Object: Model{id='2'}
Object: Model{id='3'}
Required Maven Dependency (pom.xml):
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
If you're OK with including a dependency, check out Methanol (disclaimer: I'm the library's author). The library has special BodyHandler implementations for object mapping. You can add JSON support by installing the jackson adapter.
var request = MutableRequest.GET("https://example.com")
.header("Accept", "application/json");
var modelResponse = client.send(request, MoreBodyHandlers.ofObject(Model.class));
// Use TypeRef<T> for complex types
var modelListResponse = client.send(request, MoreBodyHandlers.ofObject(new TypeRef<List<Model>>() {}));
Hi I am pretty new to Spring and Angular.I am building a spring java server and an angular client. Basically , i want the Client to be able to catch the exception throw out from the server. I defined a CustomExeption.java class and have an CustomRestExcepotionHandler.java on serverside. Right now I am not sure where should i throw out the exception in the server for the client to catch.
I was following the tutorial : https://www.baeldung.com/global-error-handler-in-a-spring-rest-api
Now it returns me with 500 Internal Server Error error message to the client side in HttpErrorResponse.
I want it to return my customexception message. Could someone help me to see if server side code has any problem. why did the HttpErrorResponse not catching the CustomException throw out? Thanks!
public class ApiError {
private HttpStatus status;
private String message;
private List<String> errors;
public ApiError(HttpStatus status, String message, List<String> errors) {
super();
this.status = status;
this.message = message;
this.errors = errors;
}
public ApiError(HttpStatus status, String message, String error) {
super();
this.status = status;
this.message = message;
errors = Arrays.asList(error);
}
public HttpStatus getStatus() {
// TODO Auto-generated method stub
return status;
}
public String getMessage() {
// TODO Auto-generated method stub
return message;
}
}
---
--------------------ExceptionHandler
#ControllerAdvice
public class CustomRestExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers,
HttpStatus status, WebRequest request) {
ApiError apiError =
new ApiError(status, ex.getMessage(), ex.getMessage());
return handleExceptionInternal(
ex, apiError, headers, apiError.getStatus(), request);
}
protected ResponseEntity<Object> handleResponseStatusException(ResponseStatusException ex,Object body, HttpHeaders headers,
HttpStatus status, WebRequest request ){
ApiError apiError =
new ApiError(status, ex.getMessage(), ex.getMessage());
return handleExceptionInternal(
ex, apiError, headers, apiError.getStatus(), request);
}
}
public ResponseEntity<AtlasJWT> signInUser(String userName, String password) {String userId = "(uid=" + userName + ")";
if (ldapTemplate.authenticate("", userId, password)) {
log.info("ldapTemplate.authenticate returned true");
Optional<AtlasUser> optLoggedInUser = userRepository.findByUsername(userName);
AtlasJWT atlasJwtToken = jwtTokenProvider.createAtlasJwtToken(optLoggedInUser.get());
if (optLoggedInUser.isPresent()) {
log.info("Atlas JWT: {}", atlasJwtToken);
return new ResponseEntity<AtlasJWT>(atlasJwtToken, HttpStatus.OK);
} else {
//ApiError error = new ApiError(HttpStatus.BAD_REQUEST,"No such User found in the Atlas Database","No such User found in the Atlas Database");
throw new CustomException("No such User found in the Atlas Database",HttpStatus.FORBIDDEN);
}
} else {
//ApiError error = new ApiError(HttpStatus.FORBIDDEN,"Invalid username/password supplied","Invalid username/password supplied");
throw new CustomException("Invalid username/password supplied", HttpStatus.FORBIDDEN);
}
}
my Client side login Component is like below:
login(username: string, password: string) {
console.log('Inside AuthenticationService. Username: ', username);
// const body = `username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}&grant_type=password`;
const body = {
'username': username,
'password': password
};
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
})
};
console.log('Invoking server authentication for username', username);
return this.http.post<AtlasJWT>('/auth/api/signin', body, httpOptions).pipe(catchError(this.handleError));
}
private handleError(err: HttpErrorResponse) {
// in a real world app, we may send the server to some remote logging infrastructure
// instead of just logging it to the console
let errorMessage = '';
if (err.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
errorMessage = err.message;
// console.log(err);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
errorMessage = `Server returned code: ${err.status}, error message is: ${err.message}`;
console.log(err);
}
console.error(errorMessage);
return throwError(errorMessage);
}
I feel like this helped. Added the annotation of #ResponseBody And #ResponseStatus.
And i also try this code , added in my controller class.both working
#ExceptionHandler(CustomException.class)
public HttpEntity<String> exceptionHandler(CustomException custEx ) {
return new HttpEntity<String>(custEx.getMessage()) ;
}
I have a scenario to create a rest method which return json response if there is any validation fail. and if everything is correct then download a pdf method.
Is it possible to be done via same method?
PFB the method signature what i need to achieve.
#RequestMapping(value = "/getPdf", method = RequestMethod.POST, consumes = MediaType.ALL_VALUE, produces = {"application/pdf","application/json"})
public #ResponseBody LinkedHashMap<String, Object> getPdf(#Valid #RequestBody DownloadPdfDTO downloadPdfDTO,BindingResult result,HttpServletRequest request,HttpServletResponse response) throws IOException {
}
This Method works fine when everything correct and PDF is getting download.
But when there is any validation fail then no response is getting render and i am getting Status : 406 Not Acceptable.
Thanks
Yes u can dot it using global exception handler.
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
public class GlobalExceptionHandler {
ObjectMapper objectMapper = new ObjectMapper();
#ExceptionHandler(AccessDeniedException.class)
public void handleAccessDenied(HttpServletResponse response, AccessDeniedException ex) {
response.setStatus(HttpStatus.UNAUTHORIZED);
try (PrintWriter writer = response.getWriter()) {
objectMapper.writeValue(writer, YOUR_OBJECT_TO_RETURN);
} catch (IOException ex) {
}
}
#ExceptionHandler({MethodArgumentNotValidException.class, ConstraintViolationException.class})
public void handleException(HttpServletResponse response, Exception ex) {
response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY);
try (PrintWriter writer = response.getWriter()) {
objectMapper.writeValue(writer, YOUR_OBJECT_TO_RETURN);
} catch (IOException ex) {
}
}
#ExceptionHandler(NullPointerException.class)
public void handleNullPointerException(HttpServletResponse response, Exception ex) {
//return your required response
}
}
You can give a return type as ResponseEntity. With ResponseEntity you can bind different response as per your business logic.
An example will be like this
#ApiOperation(value = "Get PDF API", consumes = "application/json", httpMethod = "GET")
#ApiResponses(value = {#ApiResponse(code = 404, message = "Bad request")})
#RequestMapping(value = "/pdf/{filename}", headers = "Accept=application/json", method = RequestMethod.GET)
public
#ResponseBody
ResponseEntity getPDF(#ApiParam(value = "filename", required = true) #PathVariable String filename) {
try {
Boolean validationPass = checkValidation();
if (validationPass) {
resp = getPDF();
responseHeaders.setContentType(MediaType.APPLICATION_PDF);
responseHeaders.set("charset", "utf-8");
responseHeaders.setContentLength(resp.length);
responseHeaders.set("Content-disposition", "attachment; filename=response.pdf");
return new ResponseEntity<>(resp,responseHeaders, HttpStatus.OK);
}
else {
// create your json validation eneity here
return new ResponseEntity<>(validation entity, HttpStatus.BAD_REQUEST);
}
} catch (Exception e) {
return new ResponseEntity<>( createExceptionEntity(e) ,HttpStatus.BAD_REQUEST);
}
}