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);
}
Related
I am new in Java Spring Security and have an issue with Java Spring Security.
I have the problem that despite of using authorities and WebSecurityConfigurerAdapter
in Java Spring, users not having certain authorities can still go e.g. to /trainingsplan/**.
Concrete problem: When I am logged in as a user with only the authority "Constants.TRAINER", why am I able to access the url in the server /trainingsplan/all? This user is able to get all the training plans despite of not having the ADMIN authority :/
Do you see a mistake?
My implementation in UserPrincipalDetailsService:
package auth;
import entity.User;
import entity.UserPrincipal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import repository.UserRepository;
import role.AuthorizationService;
import java.sql.SQLException;
import java.util.List;
#Component
#Service
public class UserPrincipalDetailsService implements UserDetailsService {
private UserRepository userRepository;
#Autowired
private ApplicationContext appContext;
private AuthorizationService authService;
// todo: autowire authservice here
#Lazy
#Autowired
private PasswordEncoder encoder;
public UserPrincipalDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
this.authService = new AuthorizationService(appContext);
}
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user = this.userRepository.findByUsername(s);
// String encodedPassword = encoder.encode(user.getPassword());
//user.setPassword(encodedPassword);
//user.setPassword(Bcrypter.getInstance().encode(user.getPassword()));
// encoder.encode(user.getPassword());
try {
List<String> roles = this.authService.getRolesToString(user.getId());
UserPrincipal userPrincipal = new UserPrincipal(user, roles.toArray(new String[0]));
return userPrincipal;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
My implementation in UserPrincipal:
package entity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import role.Role;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public class UserPrincipal implements UserDetails {
private String[] roles;
private User user;
public UserPrincipal(User user, String[] rolesString) {
this.user = user;
this.roles = rolesString;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<>();
List<String> listRoles =
new ArrayList<String>(Arrays.asList(this.roles));
listRoles.forEach(p -> {
GrantedAuthority authority = new SimpleGrantedAuthority(p);
authorities.add(authority);
});
/**
// Extract list of permissions (name)
this.user.getPermissionList().forEach(p -> {
GrantedAuthority authority = new SimpleGrantedAuthority(p);
authorities.add(authority);
});
// Extract list of roles (ROLE_name)
this.user.getRoleList().forEach(r -> {
GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_" + r);
authorities.add(authority);
});
*/
return authorities;
}
#Override
public String getPassword() {
return this.user.getPassword();
}
#Override
public String getUsername() {
return this.user.getUsername();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true; //this.user.getActive() == 1;
}
}
My implementation in the SecurityConfiguration:
package auth;
import org.springframework.context.ApplicationContext;
import repository.IUserRepository;
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.http.HttpMethod;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import repository.UserRepository;
import util.Constants;
import java.util.Arrays;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final ApplicationContext context;
private UserPrincipalDetailsService userPrincipalDetailsService;
private UserRepository userRepository;
#Lazy
#Autowired
private PasswordEncoder encoder;
public SecurityConfiguration(UserPrincipalDetailsService userPrincipalDetailsService, UserRepository userRepository, ApplicationContext applicationContext) {
this.userPrincipalDetailsService = userPrincipalDetailsService;
this.userRepository = userRepository;
this.context = applicationContext;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
.addFilter(new JwtAuthenticationFilter(authenticationManager(), encoder, context))
.addFilter(new JwtAuthorizationFilter(authenticationManager(), this.userRepository, context))
.authorizeRequests()
.antMatchers(HttpMethod.POST,"/login").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers(HttpMethod.POST,"/admin/**").hasAuthority(Constants.ADMIN)
.antMatchers(HttpMethod.GET,"/admin/**").permitAll()
.antMatchers(HttpMethod.PUT,"/admin/**").hasAnyAuthority(Constants.ADMIN, Constants.TRAINER)
.antMatchers(HttpMethod.GET,"/trainingsplan/**").hasAuthority(Constants.ADMIN)
.antMatchers(HttpMethod.POST,"/trainingsplan/**").hasAuthority(Constants.ADMIN)
.antMatchers(HttpMethod.PUT,"/trainingsplan/**").hasAuthority(Constants.ADMIN)
.antMatchers(HttpMethod.GET,"/trainingsplanexecuted/**").hasAuthority(Constants.ADMIN)
.antMatchers(HttpMethod.POST,"/trainingsplanexecuted/**").hasAuthority(Constants.ADMIN)
.antMatchers(HttpMethod.PUT,"/trainingsplanexecuted/**").hasAuthority(Constants.ADMIN)
.antMatchers(HttpMethod.GET,"/subscription/**").hasAnyAuthority(Constants.TRAINER, Constants.ADMIN, Constants.CUSTOMER)
.antMatchers(HttpMethod.POST,"/subscription/**").hasAnyAuthority(Constants.TRAINER, Constants.ADMIN)
.antMatchers(HttpMethod.PUT,"/subscription/**").hasAnyAuthority(Constants.TRAINER, Constants.ADMIN)
.antMatchers(HttpMethod.GET,"/trainer/**").hasAuthority(Constants.ADMIN)
.antMatchers(HttpMethod.POST,"/trainer/**").hasAuthority(Constants.ADMIN)
.antMatchers(HttpMethod.PUT,"/trainer/**").hasAuthority(Constants.ADMIN)
.antMatchers(HttpMethod.GET,"/customer/**").hasAuthority(Constants.ADMIN)
.antMatchers(HttpMethod.POST,"/customer/**").hasAuthority(Constants.ADMIN)
.antMatchers(HttpMethod.PUT,"/customer/**").hasAuthority(Constants.ADMIN)
.and().
httpBasic().and().csrf().disable();
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.addAllowedOriginPattern("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Bean
DaoAuthenticationProvider authenticationProvider(){
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
daoAuthenticationProvider.setUserDetailsService(this.userPrincipalDetailsService);
return daoAuthenticationProvider;
}
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
I can ensure you that the authorities in the UserPrincipal are correct (in my example "TRAINER").
If there is something missing, please tell me.
I would be really thankful if someone could help me.
the above things..................
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();
}
}
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 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");
}
I am using latest version of Spring Boot and I am trying to setup StatelessAuthenticaion. So far the tutorials I've been reading are very vague and I am not sure what I am doing wrong. The tutorials I am using is...
http://technicalrex.com/2015/02/20/stateless-authentication-with-spring-security-and-jwt/
The problem with my setup is that it seems everything is running correctly except for the fact that TokenAuthenticationService::addAuthentication is never called so my token is never set and therefore it returns null when the TokenAuthenticationService::getAuthentication is called and therefore returns a 401 even when I successfully logged in (Because addAuthentication is never called to set the token in the header). I am trying to figure a way to add TokenAuthenticationService::addAuthentication but I find it quite difficult.
In the tutorial he adds something similar to WebSecurityConfig::UserDetailsService.userService into auth.userDetailsService().. The only problem I am getting with that is when I do so, it throws a CastingErrorException. It only works when I utilize UserDetailsService customUserDetailsService instead...
WebSecurityConfig
package app.config;
import app.repo.User.CustomUserDetailsService;
import app.security.RESTAuthenticationEntryPoint;
import app.security.RESTAuthenticationFailureHandler;
import app.security.RESTAuthenticationSuccessHandler;
import app.security.TokenAuthenticationService;
import app.security.filters.StatelessAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.sql.DataSource;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
#Order(2)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static PasswordEncoder encoder;
private final TokenAuthenticationService tokenAuthenticationService;
private final CustomUserDetailsService userService;
#Autowired
private UserDetailsService customUserDetailsService;
#Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private RESTAuthenticationFailureHandler authenticationFailureHandler;
#Autowired
private RESTAuthenticationSuccessHandler authenticationSuccessHandler;
public WebSecurityConfig() {
this.userService = new CustomUserDetailsService();
tokenAuthenticationService = new TokenAuthenticationService("tooManySecrets", userService);
}
#Autowired
public void configureAuth(AuthenticationManagerBuilder auth,DataSource dataSource) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").authenticated();
http.csrf().disable();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.formLogin().defaultSuccessUrl("/").successHandler(authenticationSuccessHandler);
http.formLogin().failureHandler(authenticationFailureHandler);
//This is ho
http.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService),
UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService);
}
#Bean
#Override
public CustomUserDetailsService userDetailsService() {
return userService;
}
#Bean
public TokenAuthenticationService tokenAuthenticationService() {
return tokenAuthenticationService;
}
}
The TokenAuthenticationService successfully calls the getAuthentication method but in the tutorials I read, there is no proper explanation on how addAuthentication is called
TokenAuthenticationService
package app.security;
import app.repo.User.CustomUserDetailsService;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TokenAuthenticationService {
private static final String AUTH_HEADER_NAME = "X-AUTH-TOKEN";
private final TokenHandler tokenHandler;
//This is called in my WebSecurityConfig() constructor
public TokenAuthenticationService(String secret, CustomUserDetailsService userService) {
tokenHandler = new TokenHandler(secret, userService);
}
public void addAuthentication(HttpServletResponse response, UserAuthentication authentication) {
final UserDetails user = authentication.getDetails();
response.addHeader(AUTH_HEADER_NAME, tokenHandler.createTokenForUser(user));
}
public Authentication getAuthentication(HttpServletRequest request) {
final String token = request.getHeader(AUTH_HEADER_NAME);
if (token != null) {
final UserDetails user = tokenHandler.parseUserFromToken(token);
if (user != null) {
return new UserAuthentication(user);
}
}
return null;
}
}
TokenHandler
package app.security;
import app.repo.User.CustomUserDetailsService;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.userdetails.UserDetails;
public final class TokenHandler {
private final String secret;
private final CustomUserDetailsService userService;
public TokenHandler(String secret, CustomUserDetailsService userService) {
this.secret = secret;
this.userService = userService;
}
public UserDetails parseUserFromToken(String token) {
String username = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody()
.getSubject();
return userService.loadUserByUsername(username);
}
public String createTokenForUser(UserDetails user) {
return Jwts.builder()
.setSubject(user.getUsername())
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
}
In my WebServiceConfig. I add the following
http.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService),
UsernamePasswordAuthenticationFilter.class);
Which calls on the following class as a filter. It gets the Authentication, but there is No where where it actually adds it.
StatelessAuthenticationFilter
package app.security.filters;
import app.security.TokenAuthenticationService;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* Created by anthonygordon on 11/17/15.
*/
public class StatelessAuthenticationFilter extends GenericFilterBean {
private final TokenAuthenticationService authenticationService;
public StatelessAuthenticationFilter(TokenAuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
Authentication authentication = authenticationService.getAuthentication(httpRequest);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
SecurityContextHolder.getContext().setAuthentication(null);
}
}
The following class is what gets passed in the TokenAuthenticationService::addAuthentication
UserAuthentication
package app.security;
import app.repo.User.User;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
public class UserAuthentication implements Authentication {
private final UserDetails user;
private boolean authenticated = true;
public UserAuthentication(UserDetails user) {
this.user = user;
}
#Override
public String getName() {
return user.getUsername();
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities();
}
#Override
public Object getCredentials() {
return user.getPassword();
}
#Override
public UserDetails getDetails() {
return user;
}
#Override
public Object getPrincipal() {
return user.getUsername();
}
#Override
public boolean isAuthenticated() {
return authenticated;
}
#Override
public void setAuthenticated(boolean authenticated) {
this.authenticated = authenticated;
}
}
Thats it...
My Solution (But Need Help)...
My solution was to set the TokenAuthenticationService::addAuthentication method in my success handler... The only problem with that is the tutorial added the class TokenAuthenticationService to the WebServiceConfig class. And thats the only place its accessible. If there is a way I can obtain it in my successHandler, I might be able to set the token.
package app.security;
import app.controllers.Requests.TriviaResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by anthonygordon on 11/12/15.
*/
#Component
public class RESTAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
TriviaResponse tresponse = new TriviaResponse();
tresponse.setMessage("You have successfully logged in");
String json = ow.writeValueAsString(tresponse);
response.getWriter().write(json);
clearAuthenticationAttributes(request);
}
}
You have to call TokenAuthenticationService.addAuthentication() yourself when a user supplies their login credentials the first time.
The tutorial calls addAuthentication() in GoogleAuthorizationResponseServlet after a user successfully logs in using their Google account. Here's the relevant code:
private String establishUserAndLogin(HttpServletResponse response, String email) {
// Find user, create if necessary
User user;
try {
user = userService.loadUserByUsername(email);
} catch (UsernameNotFoundException e) {
user = new User(email, UUID.randomUUID().toString(), Sets.<GrantedAuthority>newHashSet());
userService.addUser(user);
}
// Login that user
UserAuthentication authentication = new UserAuthentication(user);
return tokenAuthenticationService.addAuthentication(response, authentication);
}
If you already have an authentication success handler, then I think you're on the right track that you need to call TokenAuthenticationService.addAuthentication() from there. Inject the tokenAuthenticationService bean into your handler and then start using it. If your success handler doesn't end up being a Spring bean then you can explicitly look tokenAuthenticationService up by calling WebApplicationContextUtils.getRequiredWebApplicationContext.getBean(TokenAuthenticationService.class).
There is also an issue in the tutorial's GitHub repo that will address the confusion between the initial login supplied by the user and the stateless authentication happening on all subsequent requests.
you can define a StatelessLoginFilter like below
.addFilterBefore(
new StatelessLoginFilter("/api/signin",
tokenAuthenticationService, userDetailsService,
authenticationManager()),
UsernamePasswordAuthenticationFilter.class)
and write the class like this
class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter {
private final TokenAuthenticationService tokenAuthenticationService;
private final UserDetailsService userDetailsService;
protected StatelessLoginFilter(String urlMapping,
TokenAuthenticationService tokenAuthenticationService,
UserDetailsService userDetailsService,
AuthenticationManager authManager) {
super(new AntPathRequestMatcher(urlMapping));
this.userDetailsService = userDetailsService;
this.tokenAuthenticationService = tokenAuthenticationService;
setAuthenticationManager(authManager);
}
#Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain,
Authentication authentication) throws IOException, ServletException {
final User authenticatedUser = userDetailsService
.loadUserByUsername(authentication.getName());
final UserAuthentication userAuthentication = new UserAuthentication(
authenticatedUser);
tokenAuthenticationService.addAuthentication(response,
userAuthentication);
SecurityContextHolder.getContext()
.setAuthentication(userAuthentication);
}
}