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();
}
}
Related
I want to create a simple login - I already created one, and it worked as is should - but when I start this server, it gives the following output:
2022-04-15 20:02:27.303 INFO 45172 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will not secure any request
This is the corresponding config-file:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final IUserService userService;
#Autowired
public SecurityConfig(IUserService userService){
this.userService = userService;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers("/mw_rest_api/**");
}
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests()
.antMatchers("/login", "/register", "/assets/**").permitAll()
.antMatchers("/", "/control-panel", "/control-panel/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.successHandler(loginSuccessHandler())
.failureHandler(loginFailureHandler())
.and()
.logout()
.permitAll()
.logoutSuccessUrl("/login");
}
}
Now I am wondering if I have forgotten something, which I dont see? Or is this a bug of Spring itself?
In configure() method you need to disable cref() then you can give your authorizeRequests() with antMatchers() I thnk every think is fine
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Autowired
private UserDetailsService userDetailsService;
#Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(NoOpPasswordEncoder.getInstance());
return provider;
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests().antMatchers("/login", "/post/blog/**", "/post/viewpost", "/authentication/create").permitAll()
.antMatchers("/post/filter/page/**", "/post/sorted/page/**", "/post/search/page/**").permitAll()
.antMatchers("/authentication/register", "/review/comment/**").permitAll()
.antMatchers("/post/newPost", "/post/publish", "/post/update", "/post/delete").hasAnyAuthority("ADMIN", "AUTHOR")
.antMatchers( "/review/updateComment", "/review/deleteComment").hasAnyAuthority("ADMIN", "AUTHOR", "USER")
.antMatchers("/rest/authenticate", "/rest/blog/**", "/rest/viewpost/**", "/rest/create").permitAll()
.antMatchers("/rest/filter/page/**", "/rest/sorted/page/**", "/rest/search/page/**", "/rest/comment").permitAll()
.antMatchers("/post/register").permitAll()
.antMatchers("/rest/newPost", "/rest/publish", "/rest/update", "/rest/delete").hasAnyAuthority("ADMIN", "AUTHOR")
.antMatchers("/rest/comment/**", "/rest/updateComment/**", "/post/deleteComment/**").hasAnyAuthority("ADMIN", "AUTHOR", "USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/authentication/login").permitAll()
.defaultSuccessUrl("/post/blog")
.and()
.logout().invalidateHttpSession(true)
.clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/authentication/logout-success").permitAll();
}
}
This is my code you can take it for reference
And I was using jwt for rest API.
Remove permitAll() after logout()
Remove permitAll() after login("...")
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);
}
}
I have few views made in Vaadin by #Route and now I want to add Security and some Login. In my SecurityConfiguration class I'm setting antMatchers.permitAll() only for 2 views and for the rest with Role ADMIN. But it is not working as I think it should. It demands login to access every view, and after login I have access to all views no matter what role has the user.
I hoped this tutorial will help me, but in there are no views accessible without login.
Securing Your App With Spring Security
My configuration class:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private UserService userService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
public SecurityConfiguration(UserService userService) {
this.userService = userService;
}
#Autowired
private void configureAuth(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
auth.inMemoryAuthentication()
.withUser("user")
.password(passwordEncoder().encode("user"))
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and()
.anonymous()
.and()
.authorizeRequests()
.antMatchers("/", "/login").permitAll()
.antMatchers("/recipe-manager", "/ingredient-manager").hasAnyRole("ADMIN")
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().logoutSuccessUrl("/")
.and()
.csrf().disable().cors().disable().headers().disable();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
"/VAADIN/**",
"/favicon.ico",
"/robots.txt",
"/manifest.webmanifest",
"/sw.js",
"/offline-page.html",
"/icons/**",
"/images/**",
"/frontend/**",
"/webjars/**",
"/h2-console/**",
"/frontend-es5/**", "/frontend-es6/**");
}
}
My Views have annotations like:
#Route("recipe-manager")
public class RecipeManagerView extends VerticalLayout
#Route("")
public class RecipeBrowserView extends VerticalLayout
#Route("login")
public class LoginView extends VerticalLayout
#Route("ingredient-manager")
public class IngredientManagerView extends VerticalLayout
I would expect that anyone can have access to RecipeBrowserView and LoginView, but only logged user can have access to RecipeManagerView and IngredientMangerView.
You cannot use path based matching from Spring Security for Vaadin routes. Spring Security does the matching based on request paths whereas navigation from one view to another inside Vaadin is sent as metadata inside an internal request that always goes to the same hardcoded path.
Instead, you can implement your access control logic in an interceptor provided by Vaadin. You can have a look at https://vaadin.com/tutorials/securing-your-app-with-spring-security to find out more about this.
To my understanding antMatchers only accept single arguments. You should change you configuration class like:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private UserService userService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
public SecurityConfiguration(UserService userService) {
this.userService = userService;
}
#Autowired
private void configureAuth(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
auth.inMemoryAuthentication()
.withUser("user")
.password(passwordEncoder().encode("user"))
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and()
.anonymous()
.and()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/recipe-manager", "/ingredient-manager").hasAnyRole("ADMIN")
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().logoutSuccessUrl("/")
.and()
.csrf().disable().cors().disable().headers().disable();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
"/VAADIN/**",
"/favicon.ico",
"/robots.txt",
"/manifest.webmanifest",
"/sw.js",
"/offline-page.html",
"/icons/**",
"/images/**",
"/frontend/**",
"/webjars/**",
"/h2-console/**",
"/frontend-es5/**", "/frontend-es6/**");
}
}
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
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();
}
}
}