I was earlier using pac4j version 2.3.1 with spring-security-pac4j 3.0.0 and it was working fine.
Now I am upgraded to pac4j version 5.3.1 and its not working properly with spring-security-pac4j 6.1.0 version, SecurityContextHolder.getContext().getAuthentication() is comming as null.
The SecurityFilter class has changes in spring-security-pac4j 6.1.0
Can you please help me:
pac4j version 5.3.1 is compatible with which versions of spring-security-pac4j
Also if i need to use spring-security-pac4j 6.1.0 version, then what changes i need to do.
Below is test case failing for me, SecurityContextHolder.getContext().getAuthentication() is null with version of spring-security-pac4j 6.1.0
HttpServletResponse response = mock(HttpServletResponse.class);
FilterChain chain = mock(FilterChain.class);
CommonSecurityFilter filter = new CommonSecurityFilter(config, "Test");
filter.doFilter(mockRequest(TEST_TOKEN), response, chain);
assertNotNull(SecurityContextHolder.getContext().getAuthentication());
CommonSecurityFilter is custom class and its not even calling doFilter method from below:
public class CommonSecurityFilter extends CompositeFilter {
public CommonSecurityFilter(Config config, String clients) {
List<Filter> filters = new ArrayList<>();
filters.add(new SecurityFilter(config, clients));
filters.add(new AuthenticationFilter());
setFilters(filters);
}
private static class AuthenticationFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
SecurityContext context = SecurityContextHolder.getContext();
Authentication auth = context.getAuthentication();
if (auth instanceof Pac4jAuthentication && auth.isAuthenticated()) {
CommonProfile profile = (CommonProfile)((Pac4jAuthentication)auth).getProfile();
if (profile instanceof CServUserProfile) {
CommonAuthenticationToken token = new CommonAuthenticationToken((CommonUserProfile)profile);
token.setAuthenticated(true);
context.setAuthentication(token);
}
}
chain.doFilter(request, response);
}
}
}
Related
I'm working on springboot project and we are using openId keycloak for authentication. I'm delaing with Multitenancy concept too. I want to sent custom header as request or either response and the same should be captured in APM as metadata. My current approach is as follows:
public class PreAuthFilter extends KeycloakPreAuthActionsFilter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
((HttpServletResponse) response).addHeader("X-Realm",realm);
super.doFilter(request, response, chain);
}
But with above code i'm getting multiple response metatdata in APM
http.response.headers.X-Realm.0
http.response.headers.X-Realm.1
http.response.headers.X-Realm.2
http.response.headers.X-Realm.3
My expectation was single realm in APM Metadata
http.response.headers.X-Realm = "value"
I think SimpleHttpFacade is getting intialized during resolving deployment multiple times hence adding the response.
Need Suggestion
Thanx.
It appears this could be that the issue is more likely related to your application context spring and filters.
Since it's spring could you try OncePerRequestFilter ?
import org.springframework.web.filter.OncePerRequestFilter;
#Named
public class ApmFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// do apm things
filterChain.doFilter(request, response);
}
#Override
public void destroy() {
}
}
I am upgrading a Spring Boot application to version 2.0 and Spring Framework to version 5.1.
The application currently uses Spring's built in JSONP support using AbstractJsonpResponseBodyAdvice.
#ControllerAdvice
public class JsonpControllerAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpControllerAdvice() {
super("jsonp");
}
}
However, JSONP support was deprecated in version 5.0.7 and removed in version 5.1 RC1. In addition, it's not feasible to switch to CORS at this time.
A final caveat is that the JavaScript callback method must begin with /**/. For example (truncated):
/**/jQuery1720351297557893959_1567180700293(...)
I've tried using jsonp-filter but I am unable to configure the callback to include /**/.
How do I create a custom Spring Boot JSONP filter with /**/ prepended to the callback?
Note: My example is similar to Spring Boot: Remove /**/ before JSONP callback function name. But I can't remove the /**/ because the existing frontend code expects it in the callback.
While you can't use jsonp-filter, you can define a simple filter based on it. For example:
#Component
public class JsonPFilter implements Filter {
#Override public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String callback = null;
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
callback = httpServletRequest.getParameter("jsonp");
}
if (callback != null) {
OutputStream out = response.getOutputStream();
out.write(String.format("/**/%s(", callback).getBytes());
chain.doFilter(request, response);
out.write(new JsonPResponseWrapper((HttpServletResponse) response).getData());
out.write(")".getBytes());
out.close();
} else {
chain.doFilter(request, response);
}
}
private static class JsonPResponseWrapper extends HttpServletResponseWrapper {
private JsonPResponseWrapper(HttpServletResponse response) {
super(response);
}
private byte[] getData() {
return new ByteArrayOutputStream().toByteArray();
}
}
}
This question already has answers here:
CORS issue - No 'Access-Control-Allow-Origin' header is present on the requested resource
(8 answers)
Closed 3 years ago.
I am trying to execute requests from angular js frontend to spring boot middle ware (spring boot 2.1.4) . The setup used to work as expected before I migrated the app to spring boot.
Post spring boot migration all the filter and security config from web XML has been configured in the form of annotated classes.
Now my requests from UI are getting rejected by spring boot with http 401 error with cors policy (Allowed-Origin)
My current project setup looks like this
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().antMatchers("/**").hasAnyRole("ROLE_USER").anyRequest()
.authenticated().and().csrf().csrfTokenRepository(csrfTokenRepository());
}
private CsrfTokenRepository csrfTokenRepository() {
CustomDomainCookieCsrfTokenRepository repository = new CustomDomainCookieCsrfTokenRepository();
repository.setCookieHttpOnly(false);
return repository;
}
}
#WebFilter("/*")
public class ForceCORSFilter extends OncePerRequestFilter {
protected final Logger log = Logger.getLogger(this.getClass());
private CacheService cacheService;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
List<String> originList = getCacheService().getValidOriginUrI();
String clientOrigin = request.getHeader("origin");
if (clientOrigin == null) {
// process the request even if origin is null
processValidRequest(request, response, filterChain, clientOrigin);
}
if (clientOrigin != null) {
// Origin should be validated if not null
if (originList.contains(clientOrigin)) {
processValidRequest(request, response, filterChain, clientOrigin);
} else {
log.info("####################### ORIGIN IS INVALID #######################" + clientOrigin);
filterChain.doFilter(request, response);
}
}
} catch (Exception e) {
response.getWriter()
.write("An error has occured while processing the request. Please retry with proper request.");
log.info("An error has occured in the request " + e.getMessage());
}
}
private void processValidRequest(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain,
String clientOriginAllowed) throws IOException, ServletException {
response.addHeader("Access-Control-Allow-Origin", clientOriginAllowed);
response.addHeader("Access-Control-Allow-Credentials", "true");
if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, HEAD");
response.addHeader("Access-Control-Allow-Headers",
"Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers,Authorization, X-XSRF-TOKEN");
} else {
filterChain.doFilter(request, response);
}
}
public CacheService getCacheService() {
return cacheService;
}
public void setCacheService(CacheService cacheService) {
this.cacheService = cacheService;
}
}
Can someone point out what is wrong here. Why I am still getting
http 401 "No 'Access-Control-Allow-Origin' header is present on the
requested resource" errors.
One issue might be precedence -- your filter isn't run at the right order. You can use #Order(Ordered.HIGHEST_PRECEDENCE) so it is run before the Spring Security filters.
Having said that, Spring has first-class support for CORS already, so there is no need to tediously define a filter at all. See the documentation and an example.
Hello I want to modify some of my API's response Headers after I have completed processing (executed logic) and have concluded with an HTTP status code.
For example if the response is 404, then include specific for example Cache-Control Headers example dont cache, or something like that.
I have already 2 OncePerRequestFilter registered, which work fine - but obviously I can not do logic - once the processing is complete. The CacheControlFilter already has logic that adds by default some Cache-Control headers - e.g cache for 15 sec etc. It seems though that this happens (the addition of headers on the response) on a very early stage of the dispatch and when it reaches to the phase of executing the actual Controller/Endpoint and there is an exception or Error that obviously is going to be handled by an advice etc, I can not mutate these already existing headers- that were already added by the filter.
#Bean
public FilterRegistrationBean filterOne() {
Filter filter = new FilterOne();
return createFilter(filter, "FilterOne",List.of("/*"));
}
#Bean
public FilterRegistrationBean cacheControlFilter() {
Filter filter = new CacheControlFilter();
return createFilter(filter, "CacheControlFilter", List.of("/*"));
}
private FilterRegistrationBean createFilter(Filter aFilter, String filterName,
List<String> urlPatterns) {
FilterRegistrationBean filterRegBean = new FilterRegistrationBean(aFilter);
filterRegBean.addUrlPatterns(urlPatterns.toArray(new String[0]));
filterRegBean.setName(filterName);
filterRegBean.setEnabled(true);
filterRegBean.setAsyncSupported(true);
return filterRegBean;
}
I have already tried, to add an HttpServletResponseWrapper as indicated on these post here and here on the CacheControlFilter but it does not seem to work. I have also seen a similar S.O thread here.
HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper(response) {
#Override
public void setStatus(int sc) {
super.setStatus(sc);
handleStatus(sc);
}
#Override
#SuppressWarnings("deprecation")
public void setStatus(int sc, String sm) {
super.setStatus(sc, sm);
handleStatus(sc);
}
#Override
public void sendError(int sc, String msg) throws IOException {
super.sendError(sc, msg);
handleStatus(sc);
}
#Override
public void sendError(int sc) throws IOException {
super.sendError(sc);
handleStatus(sc);
}
private void handleStatus(int code) {
if(code == 404)
addHeader("Cache-Control, "xxx");
}
};
But the code is not executed at all! So I want to manipulate the Cache-Control headers on the second filter only after though the processing is complete and I am ready to return a response.
I am not sure if the fact that I also have, doing some clean up and setting responses upon errors - mixes things up!
#ControllerAdvice
#Slf4j
public class GlobalErrorHandler
Update: As a note, when my Controller is throwing an Exception or Error, the above GlobalErrorHandler is invoked and there I execute a special handling, returning an error response. What I see though is that magically the response has already the default headers populated by the Filter (CacheControlFilter). So it ends up being a bit weird, I add extra logic,to change the control header and I end up with a response that has the same header 2 times (1 with the value set by the CacheControlFilter and then any special value I am trying to override on the ControllerAdvice
Any tips or help appreciated thanks! I am using Spring Boot 2.1.2 with Undertow as my underlying servlet container.
The link you mentioned says that cannot get the status code or modify the headers in ResponseBodyAdvice is not true . If you cast ServerHttpResponse to ServletServerHttpResponse , you can do both of them. So simply implement a ResponseBodyAdvice :
#ControllerAdvice
public class CacheControlBodyAdvice implements ResponseBodyAdvice {
#Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
#Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if(response instanceof ServletServerHttpResponse) {
ServletServerHttpResponse res= (ServletServerHttpResponse)(response);
if(res.getServletResponse().getStatus() == 400){
res.getServletResponse().setHeader("Cache-Control", "XXXXX");
}
}
return body;
}
}
One more thing need to pay attention is that if your controller method throws an exception before complete normally , depending on how to handle the exceptions , the ResponseBodyAdvice may not be trigger. So , I suggest to implement the same logic in the GlobalErrorHandler for safety guard :
#ControllerAdvice
public class GlobalErrorHandler{
#ExceptionHandler(value = Exception.class)
public void handle(HttpServletRequest request, HttpServletResponse response) {
if(response.getStatus() == 400){
response.setHeader("Cache-Control", "XXXXX");
}
}
}
I supposed that you are using spring-mvc (As you mentioned in your tags); If so you can bind to HttpServletResponse to add your headers. You can do it in your method handler like so:
#RestController
class HelloWordController{
#GetMapping("/hello")
public String test(HttpServletResponse response){
response.addHeader("test", "123");
return "hola";
}
}
Another solution (fashion) would be to return a ResponseEntity instead :
#RestController
class HelloWorkController{
#GetMapping("/hello")
public ResponseEntity<String> test(HttpServletResponse response){
return ResponseEntity.status(HttpStatus.OK)
.header("test", "4567")
.body("hello world");
}
}
There are a dozen of ways of changing a HttpServletResponse before return to client in Spring and injecting the response into the handler method or leveraging ControllerAdvice are valid solutions. However, I don't understand the underlying premise of your question that filters can't do the job:
I have already 2 OncePerRequestFilter registered, which work fine -
but obviously I can not do logic - once the processing is complete.
As far as modifying HttpServletResponse is concerned, Filters work totally fine for me and are at least as suitable as any other tool for that job:
#Bean
public FilterRegistrationBean createFilter() {
Filter filter = new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
super.doFilter(request, response, filterChain);
response.setHeader("Cache-Control", "xxx");
}
};
return new FilterRegistrationBean(filter);
}
Problem I want to solve:
For every call made to the service I want to check that the token is active, if it isn't active I want to redirect the user to the login page.
Current setup: Grails 3.2.9 , Keycloak 3.4.3
Ideas so far:
This article looked promising: https://www.linkedin.com/pulse/json-web-token-jwt-spring-security-real-world-example-boris-trivic
In my security config I added a token filter
#Bean
public TokenAuthenticationFilter authenticationTokenFilter() throws Exception {
return new TokenAuthenticationFilter();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure http
http
.addFilterBefore(authenticationTokenFilter(), BasicAuthenticationFilter.class)
.logout()
.logoutSuccessUrl("/sso/login") // Override Keycloak's default '/'
.and()
.authorizeRequests()
.antMatchers("/assets/*").permitAll()
.anyRequest().hasAnyAuthority("ROLE_ADMIN")
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
My TokenAuthenticationFilter just prints out the request headers at the moment :
public class TokenAuthenticationFilter extends OncePerRequestFilter {
private String getToken( HttpServletRequest request ) {
Enumeration headerEnumeration = request.getHeaderNames();
while (headerEnumeration.hasMoreElements()) {
println "${ headerEnumeration.nextElement()}"
}
return null;
}
#Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String authToken = getToken( request );
}
}
Which returns:
host
user-agent
accept
accept-language
accept-encoding
cookie
connection
upgrade-insecure-requests
cache-control
The code/logic I want to implement in the filter is something like:
KeycloakAuthenticationToken token = SecurityContextHolder.context?.authentication
RefreshableKeycloakSecurityContext context = token.getCredentials()
if(!context.isActive()){
// send the user to the login page
}
However I'm lost as to how to get there.
Any help greatly appreciated
As far as I understand, your question is about "how to check the token is active?" and not "how to redirect the user to login page?".
As I see you added the tag "spring-boot" and "keycloak" maybe you could use "Keycloak Spring Boot Adapter". Assuming you use the version 3.4 of Keycloak (v4.0 still in beta version), you can found some documentation here.
If you can't (or don't want to) use Spring Boot Adapter, here is the part of the KeycloakSecurityContextRequestFilter source code that could be interesting for your case:
KeycloakSecurityContext keycloakSecurityContext = getKeycloakPrincipal();
if (keycloakSecurityContext instanceof RefreshableKeycloakSecurityContext) {
RefreshableKeycloakSecurityContext refreshableSecurityContext = (RefreshableKeycloakSecurityContext) keycloakSecurityContext;
if (refreshableSecurityContext.isActive()) {
...
} else {
...
}
}
and here is the (Java) source code of the getKeycloakPrincipal method:
private KeycloakSecurityContext getKeycloakPrincipal() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
Object principal = authentication.getPrincipal();
if (principal instanceof KeycloakPrincipal) {
return KeycloakPrincipal.class.cast(principal).getKeycloakSecurityContext();
}
}
return null;
}
And if you want to understand how the Authentication is set in the SecurityContextHolder, please read this piece of (Java) code from KeycloakAuthenticationProcessingFilter:
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
if (authResult instanceof KeycloakAuthenticationToken && ((KeycloakAuthenticationToken) authResult).isInteractive()) {
super.successfulAuthentication(request, response, chain, authResult);
return;
}
...
SecurityContextHolder.getContext().setAuthentication(authResult);
...
try {
chain.doFilter(request, response);
} finally {
SecurityContextHolder.clearContext();
}
}
As an alternative you could also check this github repository of dynamind:
https://github.com/dynamind/grails3-spring-security-keycloak-minimal
Hoping that can help.
Best regards,
Jocker.