How to handle 401 with spring boot with angular 8 - java

I want to be able to handle 401 and show a specific page in angular 8 but currently only showing index.html file
Things to mind
Angular is the view for the spring boot so its not a separate application
I am not using spring security. Im just using filters in spring to determine if to be authorize
This is my filter.
#Component
public class CustomSessionFilter extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) httpServletRequest;
if (!req.getRequestURI().startsWith("/sample/path")){
HttpServletResponse httpResponse = (HttpServletResponse) httpServletResponse;
httpResponse.setContentType("application/json");
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
return;
}
}
Maybe its relevant that i have a Controller that extend ErrorController
#CrossOrigin
#RestController
public class IndexController implements ErrorController {
private static final String PATH = "/error";
#RequestMapping(value = PATH)
public ModelAndView saveLeadQuery() {
return new ModelAndView("forward:/");
}
#Override
public String getErrorPath() {
return PATH;
}
}
EDIT: I didnt use spring security because i dont need to login i just have to go through specific path and do some authentication.. and there is no user for the application

I just wanted to place my 0.02$ here.
In order to secure a route in your application, you can create a Security Config that extends WebSecurityConfigurerAdapter, where you can protect certain routes through either antMatchers or mvcMathchers (second is recommended). Furthermore, there you can declare a set of roles or conditions (via Spring Expression Language) that will automatically throw a 401 Error in case a user that does not have an access is trying to access the route.
More about that you can find here https://www.baeldung.com/security-spring
As of ErrorController, I believe that controller advice would be more suitable for this use case. It pretty much intercepts all errors that you declare and can return more informative and generic response :)
More about that https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
As

Related

Migrating from Filter to Interceptor results in 403 error

In my Spring Boot application i'm handling JWTs with the following Filter:
(irrelevant code is omitted for brevity)
#Component
public class JwtFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// processing the JWT from the request
Authentication auth =
new UsernamePasswordAuthenticationToken(username, password, roles);
SecurityContextHolder.getContext().setAuthentication(auth);
filterChain.doFilter(request, response);
}
}
This works perfectly, but i would like to migrate this functionality to an interceptor instead, so i simply added the same logic to a HandlerInterceptor:
#Component
public class JwtInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
// processing the JWT from the request
Authentication auth =
new UsernamePasswordAuthenticationToken(username, password, roles);
SecurityContextHolder.getContext().setAuthentication(auth);
return true;
}
}
But in this case, i always receive 403 Forbidden response for my requests.
I've debugged the JwtInterceptor and it seems to be working properly.
preHandle() is fully executed, setAuthentication() is called with the correct auth parameter and the function returns true.
So i guess the Authentication gets "lost" after the execution of this interceptor, but i cannot figure out why and at this point i don't really know how to debug further.
I'd really appreciate any advice on how to solve this (or even just how to figure out what the problem might be).

#WebFilter(urlPatterns) not mapping http request properly

I have a function with #GetMapping(value = "/getToken") that writes json content.
#GetMapping(value = "/getToken")
public String getToken(HttpServletRequest request, HttpServletResponse response, Model model) {
// jsonObject
PrintWriter out = response.getWriter();
out.print(jsonObject);
}
Now, A user can make a GET Request to above mapping using a url like this:
localhost:8080/getToken?username="username"&password="password"
I have also created a class called CORSFilter that implements javax.servlet.Filter and i want this filter to intercept only those request that have /getToken in the request path.
#WebFilter(urlPatterns = "/getToken")
public class CORSFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//validate user and password
chain.doFilter(requestToUse, responseToUse);
}
}
Now, when I hit localhost:8080 or localhost:8080/thankyou in the browser then the above filter is getting called.
How can I stop this? I want to call above filter only if the url path is localhost:8080/getToken?username="user"&password="pass"
Unfortunately, Spring Boot doesn't honor the urlPatterns for filters declared with WebFilter. If you need to apply a pattern, you'll have to declare the filter as a bean and declare a filter registration for that bean, in a configuration class. For example:
#Bean
public FilterRegistrationBean<CORSFilter> corsFilterRegistration() {
FilterRegistrationBean<CORSFilter> filterRegistrationBean =
new FilterRegistrationBean<>(corsFilter());
filterRegistrationBean.setUrlPatterns(Collections.singleton("/getToken"));
return filterRegistrationBean;
}
#Bean
public CORSFilter corsFilter() {
return new CORSFilter();
}
Note however that Spring Boot has native support for CORS. You don't need any filter for that.
You can use interceptors- HandlerInterceptor. Please refer below url for how-to.
https://www.journaldev.com/2676/spring-mvc-interceptor-example-handlerinterceptor-handlerinterceptoradapter
If it is pure jsp based application then you need to declare it in web.xml because #WebFilter doesn't guarantee the order of execution of filters or if it is spring based application then you can use #CrossOrigin on method level or declare a bean to filter each and every url as mentioned in the below url
Spring CORS

How to access a spring-mvc flash redirectAttribute in the filter chain before the DispatcherServlet is invoked?

I have the following controller:
#Controller
#RequestMapping("/my-account")
public class AccountController {
#RequestMapping(value = "/foo/post",
method = RequestMethod.POST)
public String doPost(final RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("flashAttribute", "flashAttributeValue");
return "redirect:/my-account/foo/get";
}
#RequestMapping(value = "/foo/get",
method = RequestMethod.GET)
public void doGet(final HttpServletRequest request, final Model model) {
System.out.println("in request: " + RequestContextUtils.getInputFlashMap(request).get("flashAttribute"));
System.out.println("in model: " + model.asMap().get("flashAttribute"));
}
}
I would also like to access the flash attribute flashAttribute during the invocation of a filter in the filter chain that finally invokes springs default DispatcherServlet which in turn invokes AccountController.
public class FlashAttributeBasedFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain)
throws ServletException, IOException {
String flashAttribute = // how to access the redirectAttribute flashAttribute here?
// do something with flashAttribute ...
filterChain.doFilter(request, response);
}
The DispatcherServlet uses a org.springframework.web.servlet.FlashMapManager that handles these flash attributes, but it doesn't provide read-only access so I think I would be messing something up if I would use it in the filter. And also the FlashMapManager instance is kept in the dispatcher servlet privately.
Does anybody have an idea how I can make the redirect attribute accessible in the filter chain for the GET request succeeding the POST?
Considering that all these methods return null into my filter (I don't understand why):
RequestContextUtils.getFlashMapManager(httpRequest)
RequestContextUtils.getInputFlashMap(httpRequest)
RequestContextUtils.getOutputFlashMap(httpRequest)
I used a drastic solution: read directly the into the session (where flash attributes are stored).
CopyOnWriteArrayList<FlashMap> what = (CopyOnWriteArrayList<FlashMap>) httpRequest.getSession().getAttribute("org.springframework.web.servlet.support.SessionFlashMapManager.FLASH_MAPS");
if (what != null) {
FlashMap flashMap = what.get(0);
[read flashMap as you read a HashMap]
}
I know, this code is super ugly but at the moment I don't find another solution.
Had the same problem, following works for me.
FlashMap flashMap = new SessionFlashMapManager().retrieveAndUpdate(request, null);
flashMap.get("parameter");

How can I get Spring-Security to return a 401 response as a JSON format?

I have a ReST API to an application with all controllers found under /api/, these all return JSON responses with a #ControllerAdvice which handles all exceptions to map to JSON formatted results.
This works great as of spring 4.0 #ControllerAdvice now supports matching on annotations. What I can't work out is how to return a JSON result for a 401 - Unauthenticated and 400 - Bad Request responses.
Instead Spring is simply returning the response to the container (tomcat) which renders this as HTML. How can I intercept this and render a JSON result using the same technique that my #ControllerAdvice is using.
security.xml
<bean id="xBasicAuthenticationEntryPoint"
class="com.example.security.XBasicAuthenticationEntryPoint">
<property name="realmName" value="com.example.unite"/>
</bean>
<security:http pattern="/api/**"
create-session="never"
use-expressions="true">
<security:http-basic entry-point-ref="xBasicAuthenticationEntryPoint"/>
<security:session-management />
<security:intercept-url pattern="/api/**" access="isAuthenticated()"/>
</security:http>
XBasicAuthenticationEntryPoint
public class XBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,
authException.getMessage());
}
}
I can solve 401 by using the BasicAuthenticationEntryPoint to write directly to the output stream, but I'm not sure it's the best approach.
public class XBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
private final ObjectMapper om;
#Autowired
public XBasicAuthenticationEntryPoint(ObjectMapper om) {
this.om = om;
}
#Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
om.writeValue(httpResponse.getOutputStream(),
new ApiError(request.getRequestURI(),
HttpStatus.SC_UNAUTHORIZED,
"You must sign in or provide basic authentication."));
}
}
I am yet to figure out how to handle 400 though, I once tried a catch all controller which did work, but it seemed that sometimes it would have odd conflicting behaviour with other controllers that I don't want to revisit.
My ControllerAdvice implementation has a catch all which if spring throws any exception for bad request (400) it should in theory capture it, but it does not:
#ControllerAdvice(annotations = {RestController.class})
public class ApiControllerAdvisor {
#ExceptionHandler(Throwable.class)
public ResponseEntity<ApiError> exception(Throwable exception,
WebRequest request,
HttpServletRequest req) {
ApiError err = new ApiError(req.getRequestURI(), exception);
return new ResponseEntity<>(err, err.getStatus());
}
}
I actually found myself asking the very same question a few weeks ago - As Dirk pointed out in the comments, #ControllerAdvice will only kick in if an exception is thrown from within a controller method, so will inevitably not catch all things (I was actually trying to solve the case for a JSON response for a 404 error).
The solution I settled on, although not entirely pleasant (hopefully if you get a better answer I will change my approach) is handling the error mapping in the Web.xml - I added the following, which will override the tomcat default error pages with specific URL mappings:
<error-page>
<error-code>404</error-code>
<location>/errors/resourcenotfound</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/errors/unauthorised</location>
</error-page>
<error-page>
<error-code>401</error-code>
<location>/errors/unauthorised</location>
</error-page>
Now, if any page returns a 404, it is handled by my error controller something like this:
#Controller
#RequestMapping("/errors")
public class ApplicationExceptionHandler {
#ResponseStatus(HttpStatus.NOT_FOUND)
#RequestMapping("resourcenotfound")
#ResponseBody
public JsonResponse resourceNotFound(HttpServletRequest request, Device device) throws Exception {
return new JsonResponse("ERROR", 404, "Resource Not Found");
}
#ResponseStatus(HttpStatus.UNAUTHORIZED)
#RequestMapping("unauthorised")
#ResponseBody
public JsonResponse unAuthorised(HttpServletRequest request, Device device) throws Exception {
return new JsonResponse("ERROR", 401, "Unauthorised Request");
}
}
It all still feels pretty grim - and of course is a global handling of errors - so if you didn't always want a 404 response to be json (if you were serving a normal webapp of the same application) then it doesnt work so well. But like I said, its what I settled on to get moving, here's hoping there is a nicer way!
I have solved this by implementing my own version of HandlerExceptionResolver and subclassing DefaultHandlerExceptionResolver. It took a bit to work this out and you must override most methods, though the following has done exactly what I'm after.
Firstly the basics are to create an implementation.
public class CustomHandlerExceptionResolver extends DefaultHandlerExceptionResolver {
private final ObjectMapper om;
#Autowired
public CustomHandlerExceptionResolver(ObjectMapper om) {
this.om = om;
setOrder(HIGHEST_PRECEDENCE);
}
#Override
protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
return request.getServletPath().startsWith("/api");
}
}
And now register it in your servlet context.
This now does nothing different, but will be the first HandlerExceptionResolver to be tried and will match any request starting with a context path of /api (note: you could make this a configurable parameter).
Next, we now can override any method that spring encounters errors on, there are 15 on my count.
What I have found is that I can write to the response directly and return an empty ModelAndView object or return null if my method did not eventually handle the fault which causes the next exception resolver to be tried.
As an example to handle situations where a request binding fault occurred I have done the following:
#Override
protected ModelAndView handleServletRequestBindingException(ServletRequestBindingException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
ApiError ae = new ApiError(request.getRequestURI(), ex, HttpServletResponse.SC_BAD_REQUEST);
response.setStatus(ae.getStatusCode());
om.writeValue(response.getOutputStream(), ae);
return new ModelAndView();
}
The only disadvantage here is that because I'm writing to the stream myself I'm not using any message converters to handle writing the response, so if I wanted to support XML and JSON API's at the same time it would not be possible, fortunately I'm only interested in supporting JSON, but this same technique could be enhanced to use view resolvers to determine what to render in etc.
If anyone has a better approach I'd still be interested in knowing how to deal with this.
You should look out to set error code in ResponseEntity, as below:
new ResponseEntity<String>(err, HttpStatus.UNAUTHORIZED);
Simply catch the exception in your controller and return response you want to send.
try {
// Must be called from request filtered by Spring Security, otherwise SecurityContextHolder is not updated
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
token.setDetails(new WebAuthenticationDetails(request));
Authentication authentication = this.authenticationProvider.authenticate(token);
logger.debug("Logging in with [{}]", authentication.getPrincipal());
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (Exception e) {
SecurityContextHolder.getContext().setAuthentication(null);
logger.error("Failure in autoLogin", e);
}

How can I run common code for most requests in my Spring MVC Web App?

i.e.
I have various URLs mapped using Spring MVC RequestMapping
#RequestMapping(value = "/mystuff", method = RequestMethod.GET)
#RequestMapping(value = "/mystuff/dsf", method = RequestMethod.GET)
#RequestMapping(value = "/mystuff/eee", method = RequestMethod.GET)
etc
I want to run some common action before about 90% of my requests. These are across several controllers.
Is there anyway to do that without delving into AOP? And if I have to use aspects, any guidance on how to do this?!
Thanks!
More info:
It is to run some app specific security - we are chained to a parent security set up, which we need to read and call into, and then need to access a cookie prior to some most of ours calls, but not all.
You can use an Interceptor:
http://static.springsource.org/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-handlermapping
Interceptor is the solution. It has methods preHandler and postHandler, which will be called before and after each request respectively. You can hook into each HTTPServletRequest object and also by pass few by digging it.
here is a sample code:
#Component
public class AuthCodeInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// set few parameters to handle ajax request from different host
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
response.addHeader("Access-Control-Max-Age", "1000");
response.addHeader("Access-Control-Allow-Headers", "Content-Type");
response.addHeader("Cache-Control", "private");
String reqUri = request.getRequestURI();
String serviceName = reqUri.substring(reqUri.lastIndexOf("/") + 1,
reqUri.length());
if (serviceName.equals("SOMETHING")) {
}
return super.preHandle(request, response, handler);
}
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
super.postHandle(request, response, handler, modelAndView);
}
}
The HandlerInterceptor.preHandle() method gives you access to the request and response and also the target handler. In Spring 3.1 that will be of type HandlerMethod, which gives you access to the target controller class and method. If it helps you can try excluding entire controller classes by type name, which would be strongly typed and without specifying explicit URLs.
Another option would be created an interceptor mapped to a set of URL patterns. See the section on configuring Spring MVC in the reference documentation.

Categories

Resources