I am building a mobile app and Restful API, I want the user of the app to be able to do GET what ever resources he want without Authentication. But if he want to do POST he have to enter his username and pass.
I already made a HTTP basic Authentication by putting a filter in web.xml.
<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>org.service.RestAuthenticationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/webapi/*</url-pattern>
</filter-mapping>
and there are the classes
public class AuthenticationService {
ClientsService s = new ClientsService();
public boolean authenticate(String authCredentials) {
if (null == authCredentials)
return false;
// header value format will be "Basic encodedstring" for Basic
// authentication. Example "Basic YWRtaW46YWRtaW4="
final String encodedUserPassword = authCredentials.replaceFirst("Basic"
+ " ", "");
String usernameAndPassword = null;
try {
byte[] decodedBytes = Base64.decode(
encodedUserPassword);
usernameAndPassword = new String(decodedBytes, "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
final StringTokenizer tokenizer = new StringTokenizer(
usernameAndPassword, ":");
final String username = tokenizer.nextToken();
final String password = tokenizer.nextToken();
boolean authenticationStatus =s.auth(username, password);
return authenticationStatus;
}
}
and the filter
public class RestAuthenticationFilter implements javax.servlet.Filter {
public static final String AUTHENTICATION_HEADER = "Authorization";
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filter) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authCredentials = httpServletRequest
.getHeader(AUTHENTICATION_HEADER);
// better injected
AuthenticationService authenticationService = new AuthenticationService();
boolean authenticationStatus = authenticationService
.authenticate(authCredentials);
if (authenticationStatus) {
filter.doFilter(request, response);
} else {
if (response instanceof HttpServletResponse) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse
.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
what i need to know is : how to pass the username and password or maybe just the id of the client to the methods of Restful after the Authentication.
A solution different to my comment: you can look for the HTTP request method and then make a decision if you call a authentication method or not:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filter) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authCredentials = httpServletRequest
.getHeader(AUTHENTICATION_HEADER);
boolean authenticationStatus;
// check request method
if (((HttpServletRequest).request).getMethod().equals("GET")) {
authenticationStatus=true;
} else {
// better injected
AuthenticationService authenticationService =
new AuthenticationService();
authenticationStatus = authenticationService
.authenticate(authCredentials);
}
if (authenticationStatus) {
filter.doFilter(request, response);
} else {
if (response instanceof HttpServletResponse) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse
.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
}
Update:
With JAX-RS a possible solution for obtaining more request information may look like this:
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
#GET
#Path("offers")
#Produces("application/xml")
public YourList getOffers(#Context HttpServletRequest request)
{
System.out.println("request to "+request.getRequestURI()+" , Auth: "+request.getHeader(AUTHENTICATION_HEADER));
// more stuff for obtaining data
}
Related
I want to save request and response in String format to database when endpoint /v2 is called.
This implementation workes fine and save is correct but if i call to v1 endpoint, is muted, response is null, just 200 OK in Postman. Normally it response with json {id="123456789"} Whats wrong have i in this implementation that endpoint v1 doesnt work ? If i delete CachingRequestBodyFilter class, v1 works fine but when v2 is called, nothing saved to database.
v1 endpoint is for xml and v2 endpoint is for json format.
Meybe is better way to save request and response to db via interceptors?
#AllArgsConstructor
#Component
#Data
public class RequestInterceptor implements HandlerInterceptor {
private final RequestService requestService;
#Override
public void afterCompletion(#NonNull HttpServletRequest request, #NonNull HttpServletResponse response, #NonNull Object handler, Exception ex){
requestService.saveResponse(request, response, ex);
}
}
public void saveResponse(HttpServletRequest request, HttpServletResponse response, Exception ex) {
try {
String requestBody = getRequestAsString(request);
String responseBody = getResponseAsString(response);
buildMessages(requestBody, responseBody, ex);
} catch (IOException e) {
e.printStackTrace();
}
}
private String getRequestAsString(HttpServletRequest request) throws IOException {
ContentCachingRequestWrapper requestWrapper = (ContentCachingRequestWrapper) request;
return new String(requestWrapper.getContentAsByteArray(), requestWrapper.getCharacterEncoding());
}
private String getResponseAsString(HttpServletResponse response) throws IOException {
ContentCachingResponseWrapper responseWrapper = (ContentCachingResponseWrapper) response;
byte[] responseArray = responseWrapper.getContentAsByteArray();
String characterEncoding = responseWrapper.getCharacterEncoding();
responseWrapper.copyBodyToResponse();
return new String(responseArray, characterEncoding);
}
method buildMessages() is just a builder object which i want to save to db.
filter class:
#Component
#WebFilter(filterName = "CachingRequestBodyFilter", urlPatterns = {"/v2/*"})
public class CachingRequestBodyFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res,
FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
HttpServletResponse response = (HttpServletResponse) res;
ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response);
filterChain.doFilter(wrappedRequest, wrappedResponse);
}
}
i want to save my request and response to db when endpoint v2 is called and when v1 is called response is not null;
I have a custom filter that is used to authenticate the user. I am always getting full authentication requried error even though I have thrown a custom exception with specific message & added exception handler as well.
Code for filter:
#Slf4j
#Component
public classTokenValidationFilter extends OncePerRequestFilter {
#Autowired
private TokenValidationHelper tokenValidationHelper;
#Override
protected void doFilterInternal(HttpServletRequest servletRequest,
HttpServletResponse servletResponse,
FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest httpRequest = (HttpServletRequest)servletRequest;
HttpServletResponse httpResponse = (HttpServletResponse)servletResponse;
MultiReadRequestWrapper request = new MultiReadRequestWrapper(httpRequest);
SecurityContext context = SecurityContextHolder.getContext();
// check if already authenticated
if (context.getAuthentication() == null) {
Authentication authentication =
tokenValidationHelper.validateAndAuthenticate(request);
context.setAuthentication(authentication);
}
filterChain.doFilter(request, httpResponse);
}
}
Code for exception handler:
#ControllerAdvice
public class ExceptionHandler {
#ExceptionHandler({IrrecoverableAuthException.class})
#ResponseBody
#ResponseStatus(HttpStatus.UNAUTHORIZED)
public RegistrationErrorResponse handleInternalServerException(IrrecoverableAuthException exception) {
return getErrorResponse(exception , Category.Error exception.getMessage());
}
}
But still getting wrong message
"Full authentication access is required to access this resource"
Exception handler won't be invoked from within the filter. You can use HttpServletResponse from within the filter and write your error response manually as follows:
protected void onFailedAuthentication(
HttpServletRequest request,
HttpServletResponse response,
IrrecoverableAuthException failed) {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(failed.getStatus().getStatusCode());
try (OutputStream out = response.getOutputStream()) {
out.write(MAPPER.writeValueAsBytes(getErrorResponse())); // build the required response here
out.flush();
} catch (IOException e) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
}
}
Call this method from your filter
#Slf4j
#Component
public classTokenValidationFilter extends OncePerRequestFilter {
#Autowired
private TokenValidationHelper tokenValidationHelper;
#Override
protected void doFilterInternal(HttpServletRequest servletRequest,
HttpServletResponse servletResponse,
FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest httpRequest = (HttpServletRequest)servletRequest;
HttpServletResponse httpResponse = (HttpServletResponse)servletResponse;
MultiReadRequestWrapper request = new MultiReadRequestWrapper(httpRequest);
SecurityContext context = SecurityContextHolder.getContext();
// check if already authenticated
if (context.getAuthentication() == null) {
try {
Authentication authentication =
tokenValidationHelper.validateAndAuthenticate(request);
context.setAuthentication(authentication);
} catch(IrrecoverableAuthException ex) {
onFailedAuthentication(httpRequest, httpResponse, ex);
}
}
filterChain.doFilter(request, httpResponse);
}
}
I have the following at the beginning of many methods.
if ((user.privilege & User.Privilege.WRITE) == 0) {
session.setAttribute("alert", "You do not have permission to save.");
return new ModelAndView("redirect:/admin/home");
}
How can I extract this and put it into a separate method that is called before many other controller methods, similar to Ruby on Rails before_action :validate_privilege, [:save, :weekly_report, ...]?
I found this in the docs, but it doesn't give any examples.
https://docs.spring.io/spring-boot/docs/1.5.19.RELEASE/reference/htmlsingle/#boot-features-embedded-container-servlets-filters-listeners
I found a way to do it based on #Deadpool's answer. It seems like it is more complicated than a simple annotation would be.
#Bean
public GenericFilterBean beforeAction() {
String[] actions = new String[]{"/admin/censor", "/admin/weekly-report", "/admin/email-blast-submit"};
return new GenericFilterBean() {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
// System.out.println("requestURI:"+req.getRequestURI());
boolean found = Arrays.stream(actions).anyMatch(req.getRequestURI()::equals);
if (found) {
User user = (User) req.getSession().getAttribute("user");
if (user != null && (user.privilege & User.Privilege.WRITE) == 0) {
req.getSession().setAttribute("alert", "You do not have permission to save.");
HttpServletResponse res = (HttpServletResponse) response;
res.sendRedirect("/admin/home");
return;
}
}
chain.doFilter(request, response);
}
};
}
For the API using OAuth2, I used
#Override
public ResponseEntity<...> delete(Principal principal, #RequestBody ...) throws ApiException {
isAuthorized(principal, "ROLE_USER");
...
/** If not authorized, throw ApiException. */
private void isAuthorized(Principal principal, String role) throws ApiException {
OauthClientDetail cd = userMapper.getOauthClientDetails(principal.getName());
if (cd == null || cd.authorities == null || !cd.authorities.equals(role)) {
throw new ApiException(HttpStatus.UNAUTHORIZED, ...);
}
}
(ApiException, OauthClientDetail (POJO), and UserMapper (MyBatis) are custom classes.)
If you are looking for Filter to check each request for authorization you can use Filter
#Component
public class AuthFilter implements Filter {
#Override
public void doFilter
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
LOG.info(
"Starting a transaction for req : {}",
req.getRequestURI());
chain.doFilter(request, response);
LOG.info(
"Committing a transaction for req : {}",
req.getRequestURI());
}
// other methods
}
or GenericFilterBean
public class CustomFilter extends GenericFilterBean {
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
}
You should use Spring Security
http.regexMatcher("/*") --> your base path or sth.
.exceptionHandling()
.and()
.csrf()
.disable();
http.addFilterAfter(authenticationProcessingFilter(),BasicAuthenticationFilter.class);
I have a customized spring AuthenticationProvider class but try to intercept the HTTPServletRequest and HTTPServletResponse within the loadUserDetails method.
#Component("darnGoodAuthenticaionProvider")
public class DarnGoodAuthenticaionProvider
extends HandlerInterceptorAdapter
implements AuthenticationUserDetailsService {
private HttpServletRequest request;
private HttpServletResponse response;
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
this.request = request;
this.response = response;
// we don't want anything falling here
return true;
}
#Override
public UserDetails loadUserDetails(Authentication token)throws
UsernameNotFoundException{
.......
}
}
I know the preHandler method from HandlerIntercepterAdapter is capable to the job but how can I be sure that the preHandler method is called prior to loadUserDetails, so that I can get the request and response prepared?
Thanks
On a servlet container, each request will be handled from the moment the request is received until the response is returned by only one thread (request == current thread).
So it's a matter of putting a servlet filter BEFORE the spring security filter chain (with the filter-mapping element above the filter-mapping of spring security), and storing the request and response in the thread using a ThreadLocal variable - see also this answer.
Then on the DarnGoodAuthenticaionProvider access the request using a static method RequestResponseHolder.getRequest().
web.xml config:
<filter>
<filter-name>saveRequestResponseFilter</filter-name>
<filter-class>sample.save.request.filter.SaveRequestResponseFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>saveRequestResponseFilter</filter-name>
<url-pattern>/mobilews/*</url-pattern>
</filter-mapping>
Filter to save the request response in the thread:
public class SaveRequestResponseFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
RequestResponseHolder.setRequestResponse(req,resp);
try {
chain.doFilter(request, response);
}
finally {
RequestResponseHolder.clear();
}
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
...
}
#Override
public void destroy() {
...
}
}
Request/Response holder:
public class RequestResponseHolder {
private static ThreadLocal<HttpServletRequest> requestHolder = new ThreadLocal<HttpServletRequest>();
private static ThreadLocal<HttpServletResponse> responseHolder = new ThreadLocal<HttpServletResponse>();
public static void setRequestResponse(HttpServletRequest request, HttpServletResponse response) {
requestHolder.set(request);
responseHolder.set(response);
}
public static HttpServletRequest getServletRequest(){
return requestHolder.get();
}
public static HttpServletResponse getServletResponse() {
return responseHolder.get();
}
public static void clear() {
requestHolder.remove();
responseHolder.remove();
}
}
Obtaining the request from DarnGoodAuthenticaionProvider:
HttpServletRequest req = RequestResponseHolder.getServletRequest();
I am using Dropwizard and I would like to wrap a request object from within a filter, and gain access to that instance from my resource class
e.g. do the following
Filter.java
public class ServiceRequestExtractionFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
ProxyResourceRequest requestWrapper = new RequestWrapper(httpRequest, svcRequestData);
chain.doFilter(requestWrapper, response);
}
}
...
Resource.java
#Path("/test")
#Produces(MediaType.APPLICATION_JSON)
public class DemoResource {
#GET
#Timed
public Response get(#Context UriInfo uriInfo, #Context RequestWrapper request) {
...
this doesn't work nor do I have any reason to expect it to, but it seemed like a good way to explain what I was trying to do.
Try this ContainerFilter documented here: http://dropwizard.io/manual/core.html#jersey-filters
public class DateNotSpecifiedFilter implements ContainerRequestFilter {
#Context ExtendedUriInfo extendedUriInfo;
#Override
public ContainerRequest filter(ContainerRequest request) {
boolean methodNeedsDateHeader = extendedUriInfo.getMatchedMethod().isAnnotationPresent(DateRequired.class);
String dateHeader = request.getHeaderValue(HttpHeaders.DATE);
if (methodNeedsDateHeader && dateHeader == null) {
Exception cause = new IllegalArgumentException("Date Header was not specified");
throw new WebApplicationException(cause, Response.Status.BAD_REQUEST);
} else {
return request;
}
}
}
Just add this in you application run():
environment.jersey().getResourceConfig().getContainerRequestFilters().add(new DateNotSpecifiedFilter());