I want to restrict only one max session for a single user in my application where i am using spring boot and java based config.I used spring max session 1. But its not working for me.
This is my java based spring configuration file
package com.prcvideoplt.prc;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import com.prcvideoplt.handlers.LoginFailureHandler;
import com.prcvideoplt.handlers.LoginSuccessHandler;
import com.prcvideoplt.handlers.LogoutSuccessHandler;
import com.prcvideoplt.service.security.CompanyBasedCustomFilter;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity
#ComponentScan(basePackages = "com.prcvideoplt")
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Resource(name = "custUserDetails")
private UserDetailsService userDetailsService;
#Override
#Bean
public UserDetailsService userDetailsService() {
return super.userDetailsService();
}
#Bean(name = "passwordEncoder")
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(13);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Configuration
#Order(value = 1)
public static class UserWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Resource(name = "loginSuccessHandler")
private LoginSuccessHandler loginSuccessHandler;
#Resource(name = "loginFailureHandler")
private LoginFailureHandler loginFailureHandler;
#Resource(name = "logoutSuccesshandler")
private LogoutSuccessHandler logoutSuccesshandler;
#Autowired
DataSource dataSource;
#Autowired
UserDetailsService userDetailsService;
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**");
}
#Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
#Bean
public CompanyBasedCustomFilter authenticationFilter() throws Exception {
CompanyBasedCustomFilter authFilter = new CompanyBasedCustomFilter();
authFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/authenticate", "POST"));
authFilter.setAuthenticationSuccessHandler(loginSuccessHandler);
authFilter.setAuthenticationFailureHandler(loginFailureHandler);
authFilter.setAuthenticationManager(authenticationManager());
return authFilter;
}
#Override
#Bean(name = "authenticationManager")
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepositoryImpl = new JdbcTokenRepositoryImpl();
tokenRepositoryImpl.setDataSource(dataSource);
return tokenRepositoryImpl;
}
#Bean
public SessionRegistry sessionRegistry() {
SessionRegistry sessionRegistry = new SessionRegistryImpl();
return sessionRegistry;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers(new String[]{"/user/**"}).hasRole("USER").antMatchers("/admin/**")
.hasAnyRole(new String[]{"ADMIN", "SUB_ADMIN"}).antMatchers(new String[]{"/**"}).permitAll().anyRequest().authenticated().and()
.formLogin().loginPage("/check-url-pattern").loginProcessingUrl("/authenticate").usernameParameter("username")
.passwordParameter("password").permitAll().and().addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.rememberMe().key("rem-me-key").rememberMeParameter("remember-me").rememberMeCookieName("my-remember-me")
.tokenRepository(persistentTokenRepository()).tokenValiditySeconds(86400).and().logout().logoutUrl("/invalidate")
.logoutSuccessHandler(logoutSuccesshandler).invalidateHttpSession(true).and().headers().frameOptions().sameOrigin().and()
.sessionManagement().maximumSessions(1).expiredUrl("/expired").maxSessionsPreventsLogin(true).sessionRegistry(sessionRegistry());
}
}
}
Please suggest a solution.
here's how you do it in a simple spring boot way,Go to your WebSecurityConfig Class in your case it's called SpringSecurityConfig
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true);
}
you have to remove
#Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
Instead of merely creating HttpSessionEventPublisher make sure you register it as a session event listener:
#Bean
public static ServletListenerRegistrationBean httpSessionEventPublisher() {
return new ServletListenerRegistrationBean(new HttpSessionEventPublisher());
}
Hope this helps!
You can try this in your webConfigSecurity class
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/")
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.expiredUrl("/login?invalid-session=true");
}
Related
I want to require authentication for my endpoints that start with "/chat/..." (on GET requisitions). I have done some basic configuration but it doesn't seem to be working. Even after authenticating a user and using its generated JWT Token, Postman still returns code 403 (Forbidden) for me. What could I be doing wrong?
My SecurityConfig class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private Environment env;
#Autowired
private JWTUtil jwtUtil;
private static final String[] PUBLIC_MATCHERS_GET = {
"/usuarios/**",
};
private static final String[] PUBLIC_MATCHERS = {
"/login/**"
};
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable();
http.authorizeRequests()
.antMatchers(HttpMethod.GET, PUBLIC_MATCHERS_GET).permitAll()
.antMatchers(PUBLIC_MATCHERS).permitAll()
.antMatchers(HttpMethod.GET, "/chat/**").authenticated()
.anyRequest().authenticated();
http.addFilter(new JWTAuthenticationFilter(authenticationManager(), jwtUtil));
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
My ChatController class:
import com.example.chatonline.domain.Mensagem;
import com.example.chatonline.domain.Usuario;
import com.example.chatonline.services.ChatService;
import com.example.chatonline.services.UsuarioService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#RestController
#RequestMapping(value = "/chat")
public class ChatController {
#Autowired
private ChatService chatService;
#Autowired
UsuarioService usuarioService;
#PreAuthorize("isAuthenticated()")
#GetMapping(value = "/usuarios")
public ResponseEntity<List<Usuario>> findOnlineUsers(){
List<Usuario> usuarios = usuarioService.findByOnlineUsers();
return ResponseEntity.ok().body(usuarios);
}
#PreAuthorize("isAuthenticated()")
#GetMapping(value = "/mensagens")
public ResponseEntity<List<Mensagem>> findMessages(){
List<Mensagem> obj = chatService.findMessages();
return ResponseEntity.ok().body(obj);
}
#PostMapping
public ResponseEntity<Void> insertMessage(#RequestBody Mensagem obj){
obj = chatService.insertMessage(obj);
return ResponseEntity.status(HttpStatus.CREATED).build();
}
#RequestMapping(value = "/mensagens/{id}", method = RequestMethod.DELETE)
public ResponseEntity<Void> deleteMessage(#PathVariable Integer id){
chatService.deleteMessage(id);
return ResponseEntity.noContent().build();
}
}
I am trying to update Spring Boot application from 2.4 to 2.6.4 but I am getting following error:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| webSecurityConfig
↑ ↓
| org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration.
└─────┘
Following is WebSecurityConfig code:
import javax.sql.DataSource;
import com.jay.liqui.jwt.JWTAuthorizationFilter;
import com.jay.liqui.jwt.JwtTokenProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.core.annotation.Order;
#Configuration
//#Order(1)
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private JwtTokenProvider jwtTokenProvider;
#Bean
public static PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
//Cross-origin-resource-sharing: localhost:8080, localhost:4200(allow for it.)
http.cors().and()
.authorizeRequests()
//These are public paths
.antMatchers("/resources/**", "/error", "/api/user/**").permitAll()
//These can be reachable for just have admin role.
.antMatchers("/api/admin/**").hasRole("ADMIN")
//All remaining paths should need authentication.
.anyRequest().fullyAuthenticated()
.and()
//logout will log the user out by invalidated session.
.logout().permitAll()
.logoutRequestMatcher(new AntPathRequestMatcher("/api/user/logout", "POST"))
.and()
//login form and path
.formLogin().loginPage("/api/user/login").and()
//enable basic authentication
.httpBasic().and()
//We will handle it later.
//Cross side request forgery
.csrf().disable();
//jwt filter
http.addFilter(new JWTAuthorizationFilter(authenticationManager(),jwtTokenProvider));
}
#Autowired
public void configAuthentication(AuthenticationManagerBuilder authBuilder) throws Exception {
authBuilder.jdbcAuthentication()
.dataSource(dataSource)
.passwordEncoder(new BCryptPasswordEncoder())
.usersByUsernameQuery("select username, password, enabled from usr01 where username=?")
.authoritiesByUsernameQuery("select username, role from usr01 where username=?")
;
}
//Cross origin resource sharing.
#Bean
public WebMvcConfigurer corsConfigurer(){
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedMethods("*");
}
};
}
}
The cause of this error is
Spring Boot 2.4 disable spring.main.allow-bean-definition-overriding by default and Spring Boot 2.6.4 enable
There are 2 solutions to fix it
Solution 1: You set allow-bean-definition-overriding is true in application.properties
spring.main.allow-bean-definition-overriding=true
Solution 2: You should move Bean WebMvcConfigurer to another class
Example:
#EnableWebMvc
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Autowired
LogInterceptor logInterceptor;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/swagger-ui/**").addResourceLocations("classpath:/META-INF/resources/swagger-ui.html");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(logInterceptor);
}
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedMethods("*");
}
#Bean
public InternalResourceViewResolver defaultViewResolver() {
return new InternalResourceViewResolver();
}
}
#Component
public class LogInterceptor implements HandlerInterceptor {
#Autowired
LoggingService loggingService;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (DispatcherType.REQUEST.name().equals(request.getDispatcherType().name()) && request.getMethod().equals(HttpMethod.GET.name())) {
loggingService.logRequest(request, null);
}
return true;
}
#Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
#Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
Can any one suggest me another solution or how i can fixed this issue please.and thank's by advance
cotroller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.entity.JwtRequest;
import com.example.demo.entity.JwtResponse;
import com.example.demo.service.JwtService;
#RestController
#CrossOrigin
public class JwtController {
#Autowired
private JwtService jwtService;
#PostMapping({"/authenticate"})
public JwtResponse createJwtToken(#RequestBody JwtRequest jwtRequest) throws Exception {
return jwtService.createJwtToken(jwtRequest);
}
}
service
package com.example.demo.service;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.example.demo.dao.UserDao;
import com.example.demo.Util.JwtUtil;
import com.example.demo.entity.User;
import com.example.demo.entity.JwtResponse;
import com.example.demo.entity.JwtRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.util.HashSet;
import java.util.Set;
#Service
public class JwtService implements UserDetailsService {
#Autowired
private JwtUtil jwtUtil;
#Autowired
private UserDao userDao;
#Autowired
private AuthenticationManager authenticationManager;
public JwtResponse createJwtToken(JwtRequest jwtRequest) throws Exception {
String userName = jwtRequest.getName();
String userPassword = jwtRequest.getUser_code();
authenticate(userName, userPassword);
UserDetails userDetails = loadUserByUsername(userName);
String newGeneratedToken = jwtUtil.generateToken(userDetails);
User user = userDao.findById(userName).get();
return new JwtResponse(user, newGeneratedToken);
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.findById(username).get();
if (user != null) {
return new org.springframework.security.core.userdetails.User(
user.getName(),
user.getUser_code(),
getAuthority(user)
);
} else {
throw new UsernameNotFoundException("User not found with username: " + username);
}
}
private Set getAuthority(User user) {
Set<SimpleGrantedAuthority> authorities = new HashSet<>();
user.getRoles().forEach(role -> {
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleName()));
});
return authorities;
}
private void authenticate(String userName, String userPassword) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userName, userPassword));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED");
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS");
}
}
}
WebSecurityConfiguration
package configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
private UserDetailsService jwtService;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors();
httpSecurity.csrf().disable()
.authorizeRequests().antMatchers("/authenticate", "/registerNewUser").permitAll()
.antMatchers(HttpHeaders.ALLOW).permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
;
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(jwtService).passwordEncoder(passwordEncoder());
}
}
error
Description:
Field authenticationManager in com.example.demo.service.JwtService required a bean of type 'org.springframework.security.authentication.AuthenticationManager' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'org.springframework.security.authentication.AuthenticationManager' in your configuration.
i too had a same problem i fixed by
declaring name of the bean
#Bean(name= BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
I am trying to secure a REST endpoint via the #Secured annotation of Spring Security. My main application (Spring Boot App) with the security config and the rest controller are in different packages and project.
Main app package: com.myapp.api.web
Rest controller packge: com.myapp.api.rest
Mainapp:
#SpringBootApplication
#ComponentScan(basePackages = "com.myapp.api")
#EntityScan("com.myapp.api")
#RestController
public class ApiApplication extends SpringBootServletInitializer
{
public static void main(String[] args)
{
SpringApplication.run(ApiApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
{
return application.sources(ApiApplication.class);
}
}
Security Config:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true,
securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
private static final String USERS_CONFIG_FILE_NAME = "users.yml";
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.httpBasic()
.and()
.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth,
InMemoryUserDetailsManager inMemoryUserDetailsManager, PasswordEncoder passwordEncoder) throws Exception
{
auth.userDetailsService(inMemoryUserDetailsManager).passwordEncoder(passwordEncoder);
}
#Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() throws IOException
{
return new InMemoryUserDetailsManager(
PropertiesLoaderUtils.loadAllProperties(USERS_CONFIG_FILE_NAME, getClass().getClassLoader()));
}
}
Rest controller:
#RestController
public class RestController
{
private final RestService service;
#PostMapping("/rest/v1")
#Secured({"ROLE_ADMIN"})
public List<String> getStates(#RequestBody List<String> Ids)
{
...
}
My rest endpoint is working as long as I am not setting securedEnabled = true. After setting it true I am getting a 404 Not Found as respond message. I've already debugged it and found out that the Spring Security somewhen stops in the filter chain and that the request never reaches the controller.
As far as I tested it, as long as the rest controller is in a different project this error will occure. After moving it to the same project it is working as it should.
Is there something missing in my Securityconfig or what could the problem be?
I am able to get values by your code , I have only changed Password Encoder to default and changed inMemoryAuthentication .
I did it as I don't have your file "users.yml" If you can share a sample , we will look in it , But below is my code. I kept all logic in 2 files just to verify.
Configuration Class
package com.myapp.api.web.Api;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true,
securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
private static final String USERS_CONFIG_FILE_NAME = "users.yml";
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.httpBasic()
.and()
.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new PasswordEncoder() {
#Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
#Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return rawPassword.toString().equals(encodedPassword);
}
};
}
#Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
builder.inMemoryAuthentication()
.withUser("user").password("user").roles("USER")
.and().withUser("admin").password("admin").roles("ADMIN");
}
/*
#Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() throws IOException
{
return new InMemoryUserDetailsManager(
PropertiesLoaderUtils.loadAllProperties(USERS_CONFIG_FILE_NAME, getClass().getClassLoader()));
}*/
}
Main Class
package com.myapp.api.web.Api;
import java.util.List;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
#ComponentScan(basePackages = "com.myapp.api")
#RestController
#SpringBootApplication
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
#PostMapping("/rest/v1")
#Secured({"ROLE_ADMIN"})
public List<String> getStates(#RequestBody List<String> Ids)
{
return Ids;
// ...
}
}
I have an OAuth2 implementation that is working fine for the grant type = password. Now I need to add a logic of restricting the same user/password combination to be allowed to login again if the user is logged in earlier.
For that, I researched and figured that I to create a new class (MyDefaultTokenService) that extends the DefaultTokenServices class and then add my logic in the overriden createAccessToken method. But for some reason when I debug and test, I dont hit the breakpoints placed in MyDefaultTokenService class. It always hits the Springboot's DefaultTokenServices class. I dont know where I am going wrong, could somebody please.
AuthorizationConfiguration.java
package com.company.config;
import java.util.Arrays;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import com.alcord.enums.Authorities;
import com.alcord.model.Account;
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter
implements EnvironmentAware {
private static final String ENV_OAUTH = "authentication.oauth.";
private static final String PROP_CLIENTID = "clientid";
private static final String PROP_SECRET = "secret";
private static final String PROP_TOKEN_VALIDITY_SECONDS = "tokenValidityInSeconds";
private RelaxedPropertyResolver propertyResolver;
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer()));
endpoints.tokenStore(tokenStore()).tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient(propertyResolver.getProperty(PROP_CLIENTID)).scopes("read", "write")
.authorities(Authorities.ROLE_ADMIN.name(), Authorities.ROLE_DRIVER.name(),
Authorities.ROLE_PASSENGER.name())
.authorizedGrantTypes("password", "refresh_token", "authorization_code", "implicit")
.secret(propertyResolver.getProperty(PROP_SECRET)).accessTokenValiditySeconds(
propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 1800));
}
#Override
public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
MyTokenService tokenService = new MyTokenService();
tokenService.setTokenStore(tokenStore());
tokenService.setSupportRefreshToken(true);
tokenService.setTokenEnhancer(tokenEnhancer());
return tokenService;
}
class MyTokenService extends DefaultTokenServices {
public MyTokenService() {
}
#Override
public OAuth2AccessToken readAccessToken(String accessToken) {
return super.readAccessToken(accessToken);
}
#Override
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
OAuth2AccessToken token = super.createAccessToken(authentication);
Account account = (Account) authentication.getPrincipal();
// This is where I will add my logic when it hits the breakpoint.
return token;
}
#Override
public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
throws AuthenticationException {
OAuth2AccessToken token = super.refreshAccessToken(refreshTokenValue, tokenRequest);
return token;
}
}
}
Resource Server Configuration
package com.company.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import com.alcord.security.CustomAuthenticationEntryPoint;
import com.alcord.security.CustomLogoutSuccessHandler;
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
// The DefaultTokenServices bean provided at the AuthorizationConfig
#Autowired
private DefaultTokenServices tokenServices;
// The TokenStore bean provided at the AuthorizationConfig
#Autowired
private TokenStore tokenStore;
#Autowired
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
#Autowired
private CustomLogoutSuccessHandler customLogoutSuccessHandler;
#Override
public void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint).and().logout()
.logoutUrl("/oauth/logout").logoutSuccessHandler(customLogoutSuccessHandler).and().csrf().disable()
.headers().frameOptions().disable().exceptionHandling().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll().antMatchers("/api/v1/login/**").permitAll()
.antMatchers("/api/v1/admin/**").permitAll().antMatchers("/api/v1/test/**").permitAll()
.antMatchers("/oauth/token").permitAll().antMatchers("/api/**").authenticated();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.tokenServices(tokenServices).tokenStore(tokenStore).resourceId("oauth2_id");
}
}
The answer to this: needed a call to AuthorizationServerEndpointsConfigurer.tokenServices in the configure method.
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer()));
endpoints
.tokenStore(tokenStore())
.tokenServices(tokenServices()) // missed this!
.tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager);
}