How can I switch security model in runtime so that
an existing spring security component can produce an Authentication, and
an existing spring security component can validate the Authentication
I think I resolved (2) but cannot quite figure out (1).
Spring Security configuration
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").authenticated().and()
.addFilterBefore(switchingFilter);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(switchingAuthenticationProvider);
}
#Bean
public SwitchingAuthenticationProvider switchingAuthenticationProvider() {
return new SwitchingAuthenticationProvider();
}
#Bean
public SwitchingFilter switchingFilter() {
return new SwitchingFilter();
}
}
The SwitchingAuthenticationProvider is straight forward: simply delegate to some other AuthenticationProvder (i.e., LDAP/OAUTH2 or otherwise)
(inspired by Switching authentication approaches at runtime with Spring Security).
public class SwitchingAuthenticationProvider implements AuthenticationProvider {
private AuthenticationProvider[] authProviders = // ...
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
return authProvider[i].authenticate(authentication);
}
}
But what creates the Authentication? As I understand it one option is to let the GenericFilterBean create the Authentication as illustrated below.
public class SwitchingFilter extends GenericFilterBean {
private AuthProviderService authProviders = // ...
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
Authentication authentication = authProviders.getAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
SecurityContextHolder.getContext().setAuthentication(null);
}
}
... where an AuthProviderService would delegate to something that creates an authentication. But how can I plug it with e.g., the equivalent of HttpSecurity#httpBasic() or HttpSecurity#openIdLogin()?
Bonus question: What's the difference between HttpSecurity#authenticationProvider(..) and AuthenticationManagerBuilder.authenticationProvider(..)?
It appears the Filter is responsible to create the Authentication (not sure if anything else is too).
The AnonymousAuthenticationFilter as an example
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
if (SecurityContextHolder.getContext().getAuthentication() == null) {
SecurityContextHolder.getContext().setAuthentication(
createAuthentication((HttpServletRequest) req));
}
Similar I think the SwitchingFilter should be similar to SwitchingAuthenticationProvider
public class SwitchingFilter extends GenericFilterBean {
private Filter[] filters = // ...
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
filters[i].doFilter(request, response, chain);
// do filterChain.doFilter(request, response); ??
}
}
.. for some mechanism of selecting a suitable index i.
Related
I want to Authenticate one of the post request body key-value pair, but I want to do the same with the help of a Interceptor/Filter. How can I do that?
You can create a custom request filter that will check the request:
public class MyFilter implements OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
var user = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
// do stuff you need to do here
filterChain.doFilter(request, response);
}
}
and then in your WebSecurityConfiguration class register the filter like this
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAfter(new MyFilter(), BasicAuthenticationFilter.class);
}
}
You can extend HandlerInterceptorAdapter and perform your custom operations/filters on top of request by overriding preHandle() method.
Pseudocode is here:
#Component
public class SimpleInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// Handle your request here. In your case, authentication check should go here.
return true;
}
}
Add the SimpleInterceptor to the registry to intercept the requests.
#Configuration
#EnableWebMvc
public class SimpleMvnConfigurer implements WebMvcConfigurer {
#Autowired
SimpleInterceptor simpleInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(simpleInterceptor);
}
}
That's all!
EDIT 1:
To send the response from preHandle method, follow below pseudocode:
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// Handle your request here. AIn your case, authentication check should go here.
if (!isValidAuth()) {
// Populate the response here.
try {
response.setStatus(401);
response.getWriter().write("Authentication failed.");
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
return true;
} ```
You can try this with Filter.
public class SimpleFilter implements Filter {
private void throwUnauthorized(ServletResponse res) throws IOException {
HttpServletResponse response = (HttpServletResponse) res;
response.reset();
response.setHeader("Content-Type", "application/json;charset=UTF-8");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
if (!isValidAuth(request)) {
throwUnauthorized(res);
}
chain.doFilter(req, res);
}
private boolean isValidAuth(HttpServletRequest request) {
// YOUR LOGIC GOES HERE.
return false;
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig arg0) {
}
}
Register the filter using FilterRegistrationBean
#Bean
public FilterRegistrationBean<SimpleFilter> simpleFilter() {
FilterRegistrationBean<SimpleFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new SimpleFilter());
return registrationBean;
}
Let me know if this works.
One more question about spring configuration...
I have several rest methods opened to everyone and not secured. These rest methods on server #1 are used by another server #2 in the same domain to get some data. The idea is that server #2 sets my_super_secure_cookie to some secure token and server #1 decodes and verifies it. Here is the code:
#Configuration
class SecurityConfig extends WebSecurityConfigurerAdapter {
// Some code
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/rest/public/*").permitAll()
.anyRequest().authenticated();
}
// More code
}
public class SuperSecurityFilter extends FilterSecurityInterceptor implements Filter {
public SuperSecurityFilter(String key) {
super(key);
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
Cookie[] cookies = req.getCookies();
Optional<Cookie> tokenCookie = Arrays.stream(cookies).filter(cookie -> cookie.getName().equals("my_super_secure_cookie")).findFirst();
if (tokenCookie.isPresent()) {
Cookie cookie = tokenCookie.get();
TokenCookie.create(cookie.getValue()).validate();
} else {
throw new Exception("Ooops!"));
}
chain.doFilter(req, res);
}
}
The question is how do I configure SecurityConfig to use SecurityTokenFilter on request to any of the /rest/public/* rest methods. Something like:
http
.antMatcher("/rest/public/*")
.addFilterBefore(new SuperSecurityFilter());
is not working, SuperSecurityFilter is not called on request.
p.s. I'm forced to work with this type of security model due to current business logic restrictions.
I solved (applied workaround?) the issue I have by implementing not filter, but interceptor, like this:
public class SuperSecurityInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// implementation here
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// Nothing here
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// Nothing here
}
}
And registered this interceptor in my entity extending WebMvcConfigurerAdapter. Like this:
registry.addInterceptor(new SupeSecurityInterceptor()).addPathPatterns("/rest/public/*");
Not sure if this is right thing to do though... Anyway would be glad to know about the conventional approach of implementing this type of functionality.
I am trying to configure spring security with two distinct filters.
What i want is to have some URLs which will be processed by one filter, and some URLs which will be processed by other filter.
This is the setup i came up with :
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled=true)
public class SecurityConfigurationContext {
#Order(1)
#Configuration
public static class ConfigurerAdapter1 extends WebSecurityConfigurerAdapter{
...
#Override
protected void configure(final HttpSecurity http) throws Exception {
// Handlers and entry points
http.exceptionHandling()
.authenticationEntryPoint(MyCustomAuthenticationEntryPoint);
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/filter1-urls/*").hasRole("USER");
http.addFilterBefore(myCustomFilter1, ChannelProcessingFilter.class);
http.csrf().disable();
http.httpBasic()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myCustomAuthenticationService)
.passwordEncoder(new BCryptPasswordEncoder());
}
}
#Order(2)
#Configuration
public static class ConfigurerAdapter2 extends WebSecurityConfigurerAdapter{
...
#Override
protected void configure(final HttpSecurity http) throws Exception {
// Handlers and entry points
http.exceptionHandling()
.authenticationEntryPoint(MyCustomAuthenticationEntryPoint);
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/filter2-urls/*").hasRole("SUPER_USER");
http.addFilterBefore(myCustomFilter2, ChannelProcessingFilter.class);
http.csrf().disable();
http.httpBasic()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myCustomAuthenticationService)
.passwordEncoder(new BCryptPasswordEncoder());
}
}
}
And filters look like this :
Filter1 :
#Component
#Order(1)
public final class MyCustomFilter1 implements Filter{
public MyCustomFilter1() {
super();
}
#Override
public final void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException {
// logic for processing filter1-urls
}
}
Filter2 :
#Component
#Order(2)
public final class MyCustomFilter2 implements Filter{
public MyCustomFilter2() {
super();
}
#Override
public final void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException {
// logic for processing filter2-urls
}
}
The problem is that both of these filters are invoked in a chain for every request. Any request i make, it first passes through one filter and then the other, rather then just through one.
How do i fix this ?
Thanks in advance.
You should have the only one ConfigurerAdapter. You can combine it into one configuration:
http.exceptionHandling()
.authenticationEntryPoint(MyCustomAuthenticationEntryPoint);
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/filter1-urls/*").hasRole("USER")
.antMatchers(HttpMethod.GET, "/filter2-urls/*").hasRole("SUPER_USER");
http.addFilterBefore(myCustomFilter, ChannelProcessingFilter.class);
Both of filters will be invoked because you set such behavior. You should left the only one filter to achieve the result you want.
#Component
public final class MyCustomFilter implements Filter {
public MyCustomFilter() {
super();
}
#Override
public final void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException {
// logic for processing filter2-urls and filter1-urls
}
}
Anyway your filters will be invoked in a chain. If you want to split your filters you should add if statement in your filters to filter only needed url (you can get request url from ServletRequest
#Component
public final class MyCustomFilter2 implements Filter {
public MyCustomFilter2() {
super();
}
#Override
public final void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException {
if (/*is filter2 urls*/) {
// logic for processing filter2-urls
}
}
}
I am trying to secure my Spring Rest API with token here is my custom filter
public class CustomTokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private static final Logger logger = LoggerFactory.getLogger(CustomTokenAuthenticationFilter.class);
public CustomTokenAuthenticationFilter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(defaultFilterProcessesUrl));
setAuthenticationManager(new NoOpAuthenticationManager());
setAuthenticationSuccessHandler(new TokenSimpleUrlAuthenticationSuccessHandler());
}
public final String HEADER_SECURITY_TOKEN = "X-CustomToken";
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
String token = request.getHeader(HEADER_SECURITY_TOKEN);
logger.info("token found:"+token);
AbstractAuthenticationToken userAuthenticationToken = authUserByToken(token);
if(userAuthenticationToken == null || userAuthenticationToken.getPrincipal().equals("guest")) throw new AuthenticationServiceException(MessageFormat.format("Error | {0}", "Bad Token"));
return userAuthenticationToken;
}
/**
* authenticate the user based on token
* #return
*/
private AbstractAuthenticationToken authUserByToken(String token) {
if(token==null) {
return null;
}
AbstractAuthenticationToken authToken = new MyToken(token);
try {
return authToken;
} catch (Exception e) {
logger.error("Authenticate user by token error: ", e);
}
return authToken;
}
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
super.doFilter(req, res, chain);
}
}
and here is how I configured it
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
protected AbstractAuthenticationProcessingFilter getFilter() {
return new CustomTokenAuthenticationFilter("/api/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(getFilter(), UsernamePasswordAuthenticationFilter.class)
.csrf().disable();
}
}
If you look at the getFilter(), I have passed "/api/*" as a filter processing url, but I want to configure these urls with HttpSecurity object, some thing as follows
http.authorizeRequests().antMatchers("/", "/rome").permitAll()
.antMatchers("/api/admin", "/api/newUser").access("hasRole('ADMIN')")
.antMatchers("/api/db").access("hasRole('ADMIN') or hasRole('DBA')")
Problem I see is that, the Custom filter requires a String as "filter processing url" but I do not want specify anything. That information should be passed by configuring HttpSecurity object through antMatchers etc.
Is it really possible? if yes how can I achieve that?
I used OncePerRequestFilter.
public class MyAuthenticationFilter extends OncePerRequestFilter {
// private RequestMatcher requestMatcher;
private List<RequestMatcher> includedPathMatchers = new ArrayList<>();
private List<RequestMatcher> excludedPathMatchers = new ArrayList<>();
// implement getters and setters
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
// your filter implementation and security logics
}
}
You can treat this class as a normal bean (use #Autowired and so on). Then you just need do register it in your context and inject it in the security chain.
Hope it helps.
This answer will be useful to you. It says to use setter setFilterProcessingURL() available in AbstractAuthenticationProcessingFilter
I'd like to run some code (for logging, or custom security, etc) before and/or after spring calls the controller method. I know Filters can be created to operate on ServletRequests and ServletResponses, but it's not clear how to access headers, body, query parameters, etc from those.
Most similar to what I'm trying to do is action composition in Play Framework for java.
Is there a way to do this in Spring?
Here is an example of how to inject a Header for every request using a Filter
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public final class VersionFilter implements Filter {
#Value("${version}")
protected String version;
protected FilterConfig filterConfig;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse responseAddition = (HttpServletResponse) response;
responseAddition.addHeader("X-Application-Version", this.version);
chain.doFilter(request,responseAddition);
}
#Override
public void destroy() {
}
}