Java EE how to break chain in filter and return the response - java-ee-8

How to break chain filter in java ee and return response? I don't want to execute my rest endpoint if security validation fails. For now my code just return from the function but I need send to user information what is goin on.
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest requ = (HttpServletRequest) request;
if(!valid){
return;
}
chain.doFilter(request, response);
}

One way to handle this is to use sendRedirect.
Instead of
if(!valid){
return;
}
You can have the following to display custom messages on the page e.g. mypage.action:
if(!valid){
log("Unauthorized access request");
response.sendRedirect("mypage.action");
} else {
chain.doFilter(request, response);
}

Related

CORS Unauthorized 401 error when calling spring rest api

I don't have a Spring Security implementation in my rest service, and I'm facing CORS 401 Unauthorized issue when trying to call resources in rest.
I red about it:
https://www.baeldung.com/spring-security-cors-preflight
https://docs.spring.io/spring-security/site/docs/5.0.7.RELEASE/reference/html5/#cors
I have 2 calls to rest:
1) Login
2) Others
The login functionality is based on Shiro, and when I perform login from Postman for example and then try to call other resource it works.
My problem starts when I started implementing a client side app with react and I'm trying to call the rest from fetch javascript method. I faced CORS issue when calling the login first and I solved it by adding #CorsOrigin annotation to my controller, but after login is succeeding the second call is still failing on cors 401.
If I correctly understand, CORS can be solved by adding filter t oWebSecurityConfig but if my app is working from Postman then there is no need to perform such a change in my server side app and I want to solve it from client side.
That leaves me with the option of passing the JSESSIONID in my requests, right?!
I still didn't figure out how to do it... where should I take the JSESSIONID from? I tried reading the cookies of the browser but didn't find it there... I tried taking it from Response headers of the first call to Login but the Response is coming empty!
Tried to solve it with Spring in server side but with no luck:
WebSecurityConfig:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// http
// .cors();
// .headers().disable();
// http
// .authorizeRequests()
// .requestMatchers(CorsUtils::isCorsRequest).permitAll()
// .anyRequest().authenticated()
// .and().httpBasic()
// .and().addFilterBefore(new WebSecurityCorsFilter(), ChannelProcessingFilter.class);
http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);
}
}
WebSecurityCorsFilter:
public class WebSecurityCorsFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
res.setHeader("Access-Control-Max-Age", "3600");
res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, x-requested-with, Cache-Control");
chain.doFilter(request, res);
}
#Override
public void destroy() {
}
}
Or, CorsFilter (using on of them):
#Component
//#Order(Ordered.HIGHEST_PRECEDENCE)
public class CorsFilter implements Filter{
public CorsFilter () {
super();
}
#Override
public final void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException {
final HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");
// without this header jquery.ajax calls returns 401 even after successful login and SSESSIONID being succesfully stored.
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, Authorization, Origin, Content-Type, Version");
response.setHeader("Access-Control-Expose-Headers", "X-Requested-With, Authorization, Origin, Content-Type");
final HttpServletRequest request = (HttpServletRequest) req;
if (!request.getMethod().equals("OPTIONS")) {
chain.doFilter(req, res);
} else {
// do not continue with filter chain for options requests
}
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
Thanks to this similar fetch issue: https://github.com/github/fetch/issues/386
I managed finally. So, to summarize:
1) No need for the Spring Security Config changes, except of Adding this annotation to controllers (you can add it just once to a base controller and all can inherit from it)
#CrossOrigin(origins = " * ", allowedHeaders = " * ")
2) From client side, when using fetch I had to use from both requests (Login and the second one):
credentials: 'include', //'same-origin'
(be attention that when using cors don't use the 'same-origin' value of credentials)
No need to set any cookies manually, the browser should handle it (If you need to write a Java client then it's needed... you can search for CookiesManagement java class and you'll find such implementation)

Spring security OAuth2 - invalidate session after authentication

We are securing out REST services using spring security OAuth2. Applications can call into either the /oauth/authorize, /oauth/token or /rest-api endpoints. The token and rest-api endpoints are stateless and do not need a session.
Can we invalidate the session after the user is authenticated? If so, what is the best approach. We want the user to sign-in always whenever a call to /oauth/authorize is made. Currently, calls to /oauth/authorize are skipping authentication whenever a session exists.
Understanding that the question is a bit old, I hope that the following could be helpful for those who search for the correct answer for the question
OP asked not about tokens invalidation, but how to invalidate httpSession on Spring OAuth2 server right after user authentication successfully passed and a valid access_token or authorization_code (for subsequent getting of access_token) returned to a client.
There is no out-of-the-box solution for this use-case still. But working workaround from the most active contributor of spring-security-oauth, Dave Syer, could be found here on GitHub
Just copy of the code from there:
#Service
#Aspect
public class SessionInvalidationOauth2GrantAspect {
private static final String FORWARD_OAUTH_CONFIRM_ACCESS = "forward:/oauth/confirm_access";
private static final Logger logger = Logger.getLogger(SessionInvalidationOauth2GrantAspect.class);
#AfterReturning(value = "within(org.springframework.security.oauth2.provider.endpoint..*) && #annotation(org.springframework.web.bind.annotation.RequestMapping)", returning = "result")
public void authorizationAdvice(JoinPoint joinpoint, ModelAndView result) throws Throwable {
// If we're not going to the confirm_access page, it means approval has been skipped due to existing access
// token or something else and they'll be being sent back to app. Time to end session.
if (!FORWARD_OAUTH_CONFIRM_ACCESS.equals(result.getViewName())) {
invalidateSession();
}
}
#AfterReturning(value = "within(org.springframework.security.oauth2.provider.endpoint..*) && #annotation(org.springframework.web.bind.annotation.RequestMapping)", returning = "result")
public void authorizationAdvice(JoinPoint joinpoint, View result) throws Throwable {
// Anything returning a view and not a ModelView is going to be redirecting outside of the app (I think).
// This happens after the authorize approve / deny page with the POST to /oauth/authorize. This is the time
// to kill the session since they'll be being sent back to the requesting app.
invalidateSession();
}
#AfterThrowing(value = "within(org.springframework.security.oauth2.provider.endpoint..*) && #annotation(org.springframework.web.bind.annotation.RequestMapping)", throwing = "error")
public void authorizationErrorAdvice(JoinPoint joinpoint) throws Throwable {
invalidateSession();
}
private void invalidateSession() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
HttpSession session = request.getSession(false);
if (session != null) {
logger.warn(String.format("As part of OAuth application grant processing, invalidating session for request %s", request.getRequestURI()));
session.invalidate();
SecurityContextHolder.clearContext();
}
}
}
add pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
Another solution could be to set session time out to some very small value. The simplest way to achieve that is put the following to application.yml config:
server:
session:
timeout: 1
But it's not ideal solution as the minimum value could be provider is 1 (zero is reserved for infinite sessions) and it is in minutes not in seconds
From what I understand, you are trying to programmatically logout after you have undertaken certain set of actions. Probably you should look into the SecurityContextLogoutHandler and see how it works. There is a method for logout there. I think calling it as an advice will solve your problem.
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
Assert.notNull(request, "HttpServletRequest required");
if (invalidateHttpSession) {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
}
SecurityContextHolder.clearContext();
}
First: in your configuration declare bean with token store for oauth
#Bean
#Primary
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
For controller approach we made the following class
#Controller
public class TokenController {
#RequestMapping(value = "/oauth/token/revoke", method = RequestMethod.POST)
public #ResponseBody void create(#RequestParam("token") String value) {
this.revokeToken(value);
}
#Autowired
TokenStore tokenStore;
public boolean revokeToken(String tokenValue) {
OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
if (accessToken == null) {
return false;
}
if (accessToken.getRefreshToken() != null) {
tokenStore.removeRefreshToken(accessToken.getRefreshToken());
}
tokenStore.removeAccessToken(accessToken);
return true;
}
}
If you don't wan't to use this approach you can grab current user's token autowiring Principal:
OAuth2Authentication authorization = (OAuth2Authentication) principal;
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authorization.getDetails();
String token = details.getTokenValue();
Or even autowiring OAuth2Authentication:
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
String token = details.getTokenValue();
I can offer such an option (according to #de_xtr recomendation):
import static org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes;
#Slf4j
#Component
#Aspect
public class InvalidateSessionAspect {
private final LogoutHandler logoutHandler;
public InvalidateSessionAspect() {
logoutHandler = new SecurityContextLogoutHandler();
}
#Pointcut("execution(* org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(..))")
public void postAccessTokenPointcut() {
}
#AfterReturning(value = "postAccessTokenPointcut()", returning = "entity")
public void invalidateSession(JoinPoint jp, Object entity) {
log.debug("[d] Trying to invalidate the session...");
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) currentRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
logoutHandler.logout(request, null, null);
log.debug("[d] Session has been invalidated");
}
}
And the option without any aspects:
#Slf4j
class LogoutHandlerInterceptor implements HandlerInterceptor {
#Override
public void postHandle(HttpServletRequest req, HttpServletResponse resp, Object h, ModelAndView view) {
HttpSession session = req.getSession(false);
if (session != null) {
log.debug("[d] Trying to invalidate the session...");
session.invalidate();
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(null);
SecurityContextHolder.clearContext();
log.debug("[d] Session has been invalidated");
}
}
}
#Configuration
#EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
//...
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.addInterceptor(new LogoutHandlerInterceptor())
// ...
;
}
}

jetty replay request on timeout

We are facing an issue in Jetty where on timeout it replays the original request again if we don't complete the request from async context. Here is the behavior, for every request we set a async listener with timeout, so we have 2 threads in play, one (Jetty Thread1) is listening on timeout and other (Thread2) is serving thread. Now let us say write data to client takes longer than timeout, since the request is not completed timeout thread gets triggered, it checks that someone is writing data so it returns silently. Jetty doesn't like returning silently, it replays the request back so another serving and timeout thread gets created and it goes on until data is written and async context is completed.
The code in question is here - In HttpChannelState in expired() method
if (aListeners!=null)
{
for (AsyncListener listener : aListeners)
{
try
{
listener.onTimeout(event);
}
catch(Exception e)
{
LOG.debug(e);
event.setThrowable(e);
_channel.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
break;
}
}
}
boolean dispatch=false;
synchronized (this)
{
if (_async==Async.EXPIRING)
{
_async=Async.EXPIRED;
if (_state==State.ASYNC_WAIT)
{
_state=State.ASYNC_WOKEN;
dispatch=true;
}
}
}
if (dispatch)
scheduleDispatch(); // <------------ dispatch again why
}
This is normal behaviour. You have put the request into async state and then not handled the timeout, so the request is redispatch with a DispatcherType of ASYNC.
If you add your own timeout listener and within that timeout you either complete or dispatch the asyncContext, then jetty will not redispatch it (unless your listener called dispatch).
You can also protect your async servlet code with a test for the DispatcherType, although that can be confused if you have multiple concerns that might be handled async.
asyncContext.addListener(new AsyncListener()
{
#Override
public void onTimeout(AsyncEvent event) throws IOException
{
event.getAsyncContext().complete();
}
#Override
public void onStartAsync(AsyncEvent event) throws IOException
{
}
#Override
public void onError(AsyncEvent event) throws IOException
{
}
#Override
public void onComplete(AsyncEvent event) throws IOException
{
}
});

Return HTTP Error 401 Code & Skip Filter Chains

Using a custom Spring Security filter, I'd like to return an HTTP 401 error code if the HTTP Header doesn't contain a particular key-value pair.
Example:
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
final String val = request.getHeader(FOO_TOKEN)
if(val == null || !val.equals("FOO")) {
// token is not valid, return an HTTP 401 error code
...
}
else {
// token is good, let it proceed
chain.doFilter(req, res);
}
As I understand, I could do the following:
(1) ((HttpServletResponse) res).setStatus(401) and skip the remaining filter chain
OR
(2) throw an exception that, eventually, results in Spring Security throwing a 401 error to the client.
If #1 is the better option, how can I skip the filter chain after calling setStatus(401) on the response?
Or, if #2 is the right way to go, which exception should I throw?
I suggest this solution below.
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
final String val = request.getHeader(FOO_TOKEN)
if (val == null || !val.equals("FOO")) {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_UNAUTHORIZED, "The token is not valid.");
} else {
chain.doFilter(req, res);
}
}
From the API docs for the doFilter method, you can:
Either invoke the next entity in the chain using the FilterChain object (chain.doFilter()),
or not pass on the request/response pair to the next entity in the filter chain to block the request processing
so setting the response status code and returning immediately without invoking chain.doFilter is the best option for what you want to achieve here.
Just do as they say in the upper answer. "so setting the response status code and returning immediately"
This is just type:
res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
So you can use something like this.
#Override
public void doFilter() {
if (whiteListOrigins.contains(incomeOrigin)) {
httpResponse.setHeader("Access-Control-Allow-Origin", incomeOrigin);
chain.doFilter(request, response);
} else {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_FORBIDDEN, "Not Allowed to Access. Please try with valid Origin.");
}
}

How to set custom time out for remember me cookie in grails spring security?

I have tokenValiditySeconds set in the Config.groovy as
grails.plugins.springsecurity.rememberMe.tokenValiditySeconds=31*24*60*60
However I want to set a different validity for all requests that comes from, say a sub-domain. I can identify domain info from the request object, but I am not able to override the tokenValiditySeconds from the CustomRememberMeService class.
By default the tokens will be valid for 14 days from the last
successful authentication attempt. This can be changed using
AbstractRememberMeServices.setTokenValiditySeconds(int). If this value
is less than zero, the expiryTime will remain at 14 days, but the
negative value will be used for the maxAge property of the cookie,
meaning that it will not be stored when the browser is closed.
As per the documentation, I should be able to change the validity by using setTokenValiditySeconds(int) method but it does not have any effect.
So how to override the value set in the config file?
Thanks.
Edit:
class CustomRememberMeService extends TokenBasedRememberMeServices {
def springSecurityService;
public final LoggedInUserDetails customAutoLogin(HttpServletRequest request, HttpServletResponse response) {
def cookies = request.getCookies();
if (!cookies) return null;
String rememberMeCookie = extractRememberMeCookie(request);
for (int i = 0; i < cookies.length; i++) {
Cookie c = cookies[i];
if(c.getName().equals('remember_me') && rememberMeCookie == null) {
rememberMeCookie = c.getValue();
}
}
if (rememberMeCookie == null) return null
logger.debug("rememberMeCookie is : ${rememberMeCookie}");
if (rememberMeCookie.length() == 0) {
cancelCookie(request, response);
return null;
}
String[] cookieTokens = decodeCookie(rememberMeCookie);
String username = cookieTokens[0];
def loginContext = request.getParameter('loginContext')
loginContext = (loginContext == null) ? "mainWeb" : loginContext
setTokenValiditySeconds(60) // not working
LoggedInUserDetails user = getUserDetailsService().loadUserByUsername("${username}#${request.getServerName().trim()}#${loginContext}")
springSecurityService.reauthenticate("${username}#${request.getServerName().trim()}#${loginContext}")
}
}
The resource.groovy file looks like:
//..
customRememberMeService(com.rwi.springsecurity.services.CustomRememberMeService) {
userDetailsService = ref('userDetailsService')
springSecurityService = ref('springSecurityService')
key = "${grailsApplication.config.grails.plugins.springsecurity.rememberMe.key}"
}
customRememberMeServicesFilter(CustomRememberMeServicesFilter){
authenticationManager = ref('authenticationManager')
rememberMeServices = ref('rememberMeServices')
customRememberMeService = ref('customRememberMeService')
}
//..
CustomRemeberMEService.groovy
// ..
class CustomRememberMeServicesFilter extends RememberMeAuthenticationFilter {
def customRememberMeService;
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (SecurityContextHolder.getContext().getAuthentication() == null) {
LoggedInUserDetails rememberMeAuth = customRememberMeService.customAutoLogin(request, response);
}
chain.doFilter(request, response);
}
}
Override the method calculateLoginLifetime, by default this will return the value as set in the configuration (it calls getTokenValiditySeconds(). By overriding this you can determine (based on the request) if the normal timeout should be passed or a custom one.
protected int calculateLoginLifetime(HttpServletRequest request, Authentication authentication) {
if (request.getRemoteAddr().startsWith("subdomain") {
return 15; // Or whatever you want, you could also make it configurable.
}
return getTokenValiditySeconds();
}

Resources