Is it possible to split the SecurityConfig of spring? - java

I have a project with childA and childB.
I want to configure the security of childA controllers in childA and childB controllers in childB.
So far I have the following SecurityConfig:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CookieProperties cookieProperties;
#Autowired
private LdapUserDetailsManager userDetailsService;
#Autowired
private AuthenticationSuccessHandler authenticationSuccessHandler;
#Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private AuthenticationFailureHandler authenticationFailureHandler;
#Autowired
private AccessDeniedHandler accessDeniedHandler;
#Autowired
private LogoutSuccessHandler logoutSuccessHandler;
#Autowired
private LdapProperties ldapProperties;
#Autowired
private Environment environment;
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public LdapDaoAuthenticationProvider ldapDaoAuthenticationProvider(LdapProperties ldapProperties) {
LdapDaoAuthenticationProvider provider = new LdapDaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setLdapProperties(ldapProperties);
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(ldapDaoAuthenticationProvider(ldapProperties));
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(
// how to move this in another file ?
new OrRequestMatcher(
new AntPathRequestMatcher(ChildAHttpPathStore.PATH_SOMETHING),
new AntPathRequestMatcher(ChildBHttpPathStore.PATH_SOMETHING),
)
)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
.csrf()
.csrfTokenRepository(corsCookieCsrfTokenRepository())
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, CoreHttpPathStore.PING).permitAll()
.anyRequest().hasAnyAuthority(
UserManagement.ROLE_AUTH_SERVICE
)
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.formLogin()
.loginProcessingUrl(CoreHttpPathStore.LOGIN)
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.permitAll()
.and()
.logout()
.logoutUrl(CoreHttpPathStore.LOGOUT)
.logoutSuccessUrl(CoreHttpPathStore.LOGIN_FROM_LOGOUT)
.logoutSuccessHandler(logoutSuccessHandler)
.permitAll()
.and()
.headers().cacheControl().disable();
}
#Bean(name = "userPasswordEncoder")
public LdapShaPasswordEncoder passwordEncoder() {
return new LdapShaPasswordEncoder();
}
#Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
if (null != cookieProperties.getName()) { serializer.setCookieName(cookieProperties.getName()); }
if (null != cookieProperties.getPath()) { serializer.setCookiePath(cookieProperties.getPath()); }
if (null != cookieProperties.getHttpOnly()) { serializer.setUseHttpOnlyCookie(cookieProperties.getHttpOnly()); }
if (null != cookieProperties.getMaxAge()) { serializer.setCookieMaxAge(cookieProperties.getMaxAge()); }
if (null != cookieProperties.getSecure()) { serializer.setUseSecureCookie(cookieProperties.getSecure()); }
if (null != cookieProperties.getDomain()) { serializer.setDomainName(cookieProperties.getDomain()); }
return serializer;
}
#Bean
public CorsCookieCsrfTokenRepository corsCookieCsrfTokenRepository(){
CorsCookieCsrfTokenRepository repository = new CorsCookieCsrfTokenRepository();
repository.setCookieHttpOnly(false);
repository.setHeaderName("X-XSRF-TOKEN");
repository.setCookiePath(cookieProperties.getPath());
repository.setCookieDomain(cookieProperties.getDomain());
repository.setCookieName("XSRF-TOKEN");
return repository;
}
}
Is it possible to split this configuration ?

If you need to write Multiple HttpSecurity due to spring security docs the easiest is to create a general configuration with some internal #Configuration classes for configuring HttpSecurity
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Bean
public UserDetailsService userDetailsService() throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("password").roles("USER").build());
manager.createUser(User.withUsername("admin").password("password").roles("USER","ADMIN").build());
return manager;
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
#Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
}

Related

Spring Security: Cant get really users logged in using SessionRegistry that shows expired ones

Maybe should configure session expire time or mistake is in my code?
My controller code fragment.
It shows username even if its logged out.
#GetMapping("/")
public String getProfilePage(Model model, Authentication authentication) {
if (authentication == null) {
return "redirect:/login";
}
UserDetailsImpl details = (UserDetailsImpl) authentication.getPrincipal();
model.addAttribute("user", details.getUser());
model.addAttribute("greeting", details.getUser().getGreeting());
List<UserDetails> lu = sessionRegistry.getAllPrincipals()
.stream()
.filter(principal -> principal instanceof UserDetails)
.map(UserDetails.class::cast)
.collect(Collectors.toList());
for (UserDetails l: lu){
System.out.println(l.getUsername());
}
return "Profile";
}
Cant resolve this problem for a week.
There is some answer Here but it didnt helped resolving my problem.
This is how my SecurityConfig class looks like:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Qualifier("dataSource")
#Autowired
private DataSource dataSource;
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private UserDetailsService userDetailsServiceImpl;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/expired").permitAll()
.antMatchers("/static/**").permitAll()
.antMatchers("/users").hasAnyAuthority("ADMIN")
.anyRequest().permitAll()
.and()
.formLogin().loginPage("/login")
.usernameParameter("login")
.passwordParameter("password")
.failureUrl("/login?error").permitAll()
.defaultSuccessUrl("/")
.and()
.rememberMe()
.rememberMeParameter("remember-me")
.tokenRepository(tokenRepository())
.and()
.logout()
.deleteCookies("remember-me")
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.permitAll();
http.csrf().disable();
http
.sessionManagement()
.maximumSessions(1)
// .maxSessionsPreventsLogin(false)
.sessionRegistry(sessionRegistryImpl());
}
#Bean
public SessionRegistryImpl sessionRegistryImpl() {
return new SessionRegistryImpl();
}
#Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
#Bean
public PersistentTokenRepository tokenRepository() {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
return tokenRepository;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsServiceImpl).passwordEncoder(passwordEncoder);
}
}
And added AppInitializer:
#Configuration
public class AppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addListener(HttpSessionEventPublisher.class);
}
}

Multiple authentification strategies in spring boot security

In order to use a custom authentification in spring security you got to implement the UserDetailsService interface and override the loadUserByUsername method, such as the example below
public class UserServiceImpl implements UserDetailsService{
#Autowired
private UserDao userDao;
#Override
public UserDetails loadUserByUsername(String useremail)
throws UsernameNotFoundException {
Users user = userDao.findByUserEmail(useremail);
if(user == null){
throw new UsernameNotFoundException("UserName or Password Invalid.");
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), user.getEnabled(), true, true, true, getGrantedAuthorities(userDao.getUserRole(user.getUsersId())));
}
and its working fine for the whole website.
what i want to do now is to expose a restful webservice from the same host and all the requests for that WS will be through the /api/** with a different type of authentification (e.g : using tokens)
is it possible to do it? and if so, is there any idea how to do it ? any useful resources ?
You can start by making security configuration class as follows
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final TokenAuthenticationFilter tokenAuthenticationFilter;
...
public SecurityConfiguration(TokenAuthenticationFilter tokenAuthenticationFilter) {
this.corsFilter = corsFilter;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.and()
.addFilterBefore(tokenAuthenticationFilter, TokenAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(You log out success handler goes here)
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/api/**").authenticated();
}
}
And your TokenAuthenticationFilter class will do the token authenticity for every request.
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final TokenAuthenticationFilter tokenAuthenticationFilter;
...
public SecurityConfiguration(TokenAuthenticationFilter tokenAuthenticationFilter) {
this.corsFilter = corsFilter;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.and()
.addFilterBefore(tokenAuthenticationFilter, TokenAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(You log out success handler goes here)
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/api/**").authenticated();
}
}

My question is about "considering a bean into the configuration class"

Description:
Field entryPoint in security.demo.config.SecurityConfig required a bean of type 'security.demo.config.jwt.JwtAuthenticationEntryPoint' that could not be found.
Action:
Consider defining a bean of type 'security.demo.config.jwt.JwtAuthenticationEntryPoint' in your configuration.
How can I fix this error if I have these configuration class?
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
UserService userService;
#Autowired
private JwtAuthProvider autheticationProvider;
#Autowired
private JwtAuthenticationEntryPoint entryPoint;
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Collections.singletonList(autheticationProvider));
}
//create a custom filter
#Bean
public JwtAuthFilter authTokenFilter() {
JwtAuthFilter filter =new JwtAuthFilter();
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler(new JwtSuccessHandler());
return filter;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select email as principal, password as credentials, true from users where email=?")
.authoritiesByUsernameQuery("select user_email as principal, role_name as role from user_roles where user_email=?")
.passwordEncoder(passwordEncoder()).rolePrefix("ROLE_");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/register**", "/forgot-password**", "/reset-password**").permitAll()
.antMatchers("/resources/**", "/register**").permitAll()
.antMatchers("/users", "/addTask")
.hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.defaultSuccessUrl("/profile")
.and()
.logout()
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
.and().rememberMe().key("unique-and-secret").rememberMeCookieName("remember-me-cookie-name").tokenValiditySeconds(24 * 60 * 60);
http.exceptionHandling().authenticationEntryPoint(entryPoint);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(authTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
And the JwtAuthenticationEntryPoint class is:
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest arg0, HttpServletResponse arg1, AuthenticationException arg2)
throws IOException, ServletException {
arg1.sendError(HttpServletResponse.SC_UNAUTHORIZED,"UNAUTHARIZED");
}
}
You need to add #Component over JwtAuthenticationEntryPoint class

Multiple Web Security Config Classes Filter Invocation in Spring Boot

I have a SecurityConfig class in my Spring boot 1.3.6 project which I apply a filter on HttpSecurity.
Something like below,
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#Order(1)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Inject
private TokenProvider tokenProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/logs/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/api/**").permitAll())
.and()
.apply(securityConfigurerAdapter());
}
private JWTConfig securityConfigurerAdapter() {
return new JWTConfig(tokenProvider);
}
}
public class JWTConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private TokenProvider tokenProvider;
public JWTConfig(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
#Override
public void configure(HttpSecurity http) throws Exception {
JWTFilter customFilter = new JWTFilter(tokenProvider);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
Now I have add a external dependency jar which has similar Filter registration in its own SecurityConfig.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(final WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers(HttpMethod.GET, "/health").antMatchers(HttpMethod.OPTIONS, "/**");
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.addFilterBefore(getTokenAuthenticationFilter(), BasicAuthenticationFilter.class);
http.addFilterAfter(getCorrelationIdFilter(), getTokenAuthenticationFilter.class);
http.csrf().disable().authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll();
}
private TokenAuthenticationFilter getTokenAuthenticationFilter() {
return new TokenAuthenticationFilter();
}
}
I can see the SecurityConfig class of external jar first hits and register the Filters and then project's SecurityConfig get invoke and register JWTFilter, when application start. But after latter filter registration, when I inspect HttpSecurity filters field, it contains only the JWTFilter.
Can I have all filters register in same HttpSecurity filters after those filters register with different SecurityConfigs?

Oauth2 Resource server overlap Spring Security configuration

I am trying to configure Spring Security and OAuth2 on java config. I am using Spring Security version 4.0.4.RELEASE and OAuth2 version 2.0.11.RELEASE.
Spring Security config works well. Also I can get an access token with OAuth2 AuthorizationServer, but my ResourceServer does not work correctly. When I set the annotation #EnableResourceServer I can only check my access token and other URLs I cannot open (Security configuration and AuthorizationServer configuration do not work). I see the following error:
<oauth>
<error_description>
An Authentication object was not found in the SecurityContext
</error_description>
<error>unauthorized</error>
</oauth>
If I remove an annotation #EnableResourceServer, my ResourceServer does not check an access token. It just redirects to the authentication page.
This is my code:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class GlobalSecurityConfig extends GlobalMethodSecurityConfiguration {
#Bean(name = "passwordEncoder")
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
#Qualifier("authUserDetailsService")
private UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Autowired
#Qualifier("permissionEvaluator")
private PermissionEvaluator permissionEvaluator;
#Bean
public DefaultMethodSecurityExpressionHandler expressionHandler() {
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setDefaultRolePrefix("");
handler.setPermissionEvaluator(permissionEvaluator);
return handler;
}
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return expressionHandler();
}
}
SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean(name = "clientAuthenticationEntryPoint")
public OAuth2AuthenticationEntryPoint oauthAuthenticationEntryPoint() {
OAuth2AuthenticationEntryPoint entry = new OAuth2AuthenticationEntryPoint();
entry.setRealmName("myapp/client");
entry.setTypeName("Basic");
return entry;
}
#Autowired
#Qualifier("webExpressionHandler")
private DefaultWebSecurityExpressionHandler expressionHandler;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/html/**", "/webapi/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/admin/**", "/**")
.and()
.authorizeRequests()
.expressionHandler(expressionHandler)
.antMatchers("/admin/**").access("hasRole('ADMINISTRATOR')")
.antMatchers("/1/admin/**").access("hasRole('ADMINISTRATOR')")
.antMatchers("/profile**").authenticated()
.antMatchers("/oauth/authorize").authenticated()
.and()
.formLogin().loginPage("/login")
.failureUrl("/login?error=1")
.loginProcessingUrl("/login-attempt")
.defaultSuccessUrl("/", false)
.and()
.sessionManagement()
.sessionFixation().migrateSession()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.and()
.exceptionHandling()
.accessDeniedPage("/access-denied")
.and()
.csrf();
}
}
Oauth config:
#Configuration
public class Oauth {
#Configuration
#EnableResourceServer
public static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "my_oauth_server";
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.anonymous().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.regexMatchers("/api/v0/.*").authenticated()
.antMatchers("/**").denyAll()
;
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private AuthorizationCodeServices verificationCodeService;
#Autowired
#Qualifier("clientDetails")
private ClientDetailsService clientDetailsService;
#Autowired
#Qualifier("tokenStore")
private TokenStore tokenStore;
#Bean(name = "tokenServices")
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore);
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(clientDetailsService);
return tokenServices;
}
#Bean
public ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter() throws Exception {
ClientCredentialsTokenEndpointFilter filter = new ClientCredentialsTokenEndpointFilter();
filter.setAuthenticationManager(authenticationManager);
return filter;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
endpoints.authorizationCodeServices(verificationCodeService);
endpoints.tokenServices(tokenServices());
endpoints.reuseRefreshTokens(true);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()");
oauthServer.checkTokenAccess("permitAll()");
oauthServer.realm("myapp/client");
oauthServer.addTokenEndpointAuthenticationFilter(clientCredentialsTokenEndpointFilter());
oauthServer.allowFormAuthenticationForClients();
}
}
}
So, ResourceServer config overlap other configuration. How can I fix it? I would be thankful for any help.
I see that you want to protect some endpoints with an access token, and other endpoints with normal form login.
Can you try restricting the applicability of your ResourceServerConfiguration to apply only to certain endpoints by something like: http.requestMatcher(new AntPathRequestMatcher("/api/v0/**")).... Do the same for SecurityConfig but for the endpoints you want it to take care of.

Categories

Resources