I have implemented spring security in my application.
It is stateless-token based authentication and username/password based authentication.
I have configured user authentication, but the role-based authorisation is not working.
A user who has ROLE_USER is able to access the controller method which has ROLE_ADMIN.
Here is the configuration.
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
#Configuration
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter{
#Bean
AuthenticationProvider passwordBasedAuthenticationProvider() {
return new PasswordBasedAuthenticationProvider();
}
#Bean
AuthenticationProvider tokenBasedAuthenticationProvider(){
return new TokenBasedAuthenticationProvider();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/v1/public/**");
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
csrf().disable().
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).
and().
authorizeRequests().
anyRequest().authenticated().
and().
anonymous().disable();
http.addFilterBefore(new AuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(passwordBasedAuthenticationProvider()).
authenticationProvider(tokenBasedAuthenticationProvider());
}
}
DOMAINS
#Entity
public class Role implements GrantedAuthority {
private long id;
private String authority;
}
public class User implements UserDetails{
private String username;
private String passwordHash;
private Role role;
}
#RestController
public class TesController {
#RequestMapping(value="/authController")
#Secured("ROLE_ADMIN")
String test(){ return "I am secure for ROLE_ADMIN"}
}
What is incorrect about this configuration?
You have to define at least the RoleHierarchie with something like this or whatever the configuration may look like in your case:
#Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl r = new RoleHierarchyImpl();
r.setHierarchy("ROLE_ADMIN > ROLE_STAFF");
r.setHierarchy("ROLE_STAFF > ROLE_USER");
r.setHierarchy("ROLE_DEVELOPER > ROLE_USER");
r.setHierarchy("ROLE_USER > ROLE_GUEST");
return r;
}
Related
So I am making my first spring boot project (I am making a RESTful backend API) and I came across jwt authentication. After trying a few tutorials I was keep getting stuck until one tutorial kinda helped me.
My problem:
When I run my AuthenticationController:
#RestController
#CrossOrigin
public class JwtAuthenticationController {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
private JwtUserDetailsService userDetailsService;
#RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(#RequestBody JwtRequest authenticationRequest) throws Exception {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
);} catch (BadCredentialsException e){
throw new Exception("Incorrect username or password", e);
}
final UserDetails userDetails = userDetailsService
.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity<?> saveUser(#RequestBody UserDTO user) throws Exception {
return ResponseEntity.ok(userDetailsService.save(user));
}
}
My code does not run past this piece of code:
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
);} catch (BadCredentialsException e){
throw new Exception("Incorrect username or password", e);
}
I know this because of debugging basically. It always goes to the catch and I don't know why.
When I comment out that not working piece of code, my code runs but I can use any username and password with the given jwt Token, and that is not what I want.
So I know the piece of code that makes sure my program does not run as planned is that piece.
I know the not working piece of code uses my JwtRequest so maybe anyone needs that to help me fix my problem:
JwtRequest:
package com.example.demo.Model;
public class JwtRequest{
private String username;
private String password;
public JwtRequest() {
}
public JwtRequest(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Here is my debugger and database to confirm that the credentials are correct:
My debugger:
My DataBase:
My Postman:
What I have tried:
I have tried replacing this code:
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
);} catch (BadCredentialsException e){
throw new Exception("Incorrect username or password", e);
}
With this:
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
And my according authenticate method:
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
}
But then my debugger says this:
I am basically stuck and I do not know what to do now. Can anyone help me or provide me with a tutorial on how to implement JWT authentication with an MYSQL database that is not outdated and has the source code included in the tutorial?
Thanks!
If you need some piece of code just ask and I will include it in the post.
As requested the following files:
WebSecurityConfig:
#Configuration
#AllArgsConstructor
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {//provides security for endpoints
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
private final AccountService accountService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
.authorizeRequests()
.antMatchers("/authenticate","/register")
.permitAll()//any request that goes trough that end point we want to allow.
.anyRequest()
.authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
provider.setUserDetailsService(accountService);
return provider;
}
}
Password encoder:
#Configuration
public class PasswordEncoder{
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}
UserdetailsService:
#Service
public class JwtUserDetailsService implements UserDetailsService {
#Autowired
private UserDao userDao;
#Autowired
private PasswordEncoder bcryptEncoder;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
DAOUser user = userDao.findByUsername(username);
if (user == null){
throw new UsernameNotFoundException("User not found with username: " + username);
}
return new User(user.getUsername(), user.getPassword(), new ArrayList<>());
}
public DAOUser save(UserDTO user) {
DAOUser newUser = new DAOUser();
newUser.setUsername(user.getUsername());
newUser.setPassword(bcryptEncoder.bCryptPasswordEncoder().encode(user.getPassword()));
return userDao.save(newUser);
}
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
provider.setUserDetailsService(accountService);
return provider;
}
You are setting accountService as userDetailsService instead of `JwtUserDetailsService'. have you tried that?
change in JwtUserDetailsService.java
//not sure why accountService-> don't make it final
//#Autowired //missing
//private AccountService accountService;
#Autowired
private PasswordEncoder bcryptEncoder;
#Configuration
#AllArgsConstructor
//#EnableWebSecurity not needed
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {//provides security for endpoints
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private JwtUserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
//private AccountService accountService;
#Autowired
private PasswordEncoder bCryptPasswordEncoder;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
.authorizeRequests()
.antMatchers("/authenticate","/register")
.permitAll()//any request that goes trough that end point we want to allow.
.anyRequest()
.authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//.and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); You are not using filter to authenticate, you are using a controller for that
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
//provider.setUserDetailsService(accountService);
return provider;
}
}
I'm creating an application using Spring Data REST with Thymeleaf.
Initially I created my models, controllers, dao and services. All worked fine. I'm now trying to add security to my application. Right now I'm just focused on the login/logout.
I've been able to create an in memory authentication as below:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("securityDataSource")
private DataSource securityDataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// add users for in memory authentication
UserBuilder users = User.withDefaultPasswordEncoder();
auth.inMemoryAuthentication()
.withUser(users.username("paul").password("test123").roles("MEMBER", "ADMIN"))
.withUser(users.username("sandra").password("test123").roles("MEMBER", "ADMIN"))
.withUser(users.username("matthew").password("test123").roles("MEMBER"));
}
}
I want to change this to database authentication though. I'm pretty sure I can create a jdbc connection and change my config method to something like this:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(securityDataSource);
}
My problem is that I'm already accessing the database through my DAO interfaces. E.g:
public interface UserRepository extends JpaRepository<User, Integer> {
// method to sort by last name
public List<User> findAllByOrderByLastNameAsc();
}
My users table has an email and password column which will be used as the username/password.
Is it possible to also authenticate by using this in some way? I can provide additional information but am reluctant to just post everything and hope somebody will write it for me.
Since you've already created the DAO interfaces, it may be easier to create
a UserDetailsService implementation:
#Service
#NoArgsConstructor #ToString #Log4j2
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired private UserRepository userRepository = null;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
org.springframework.security.core.userdetails.User user = null;
try {
Optional<User> optional = userRepository.findBy...(username);
HashSet<GrantedAuthority> set = new HashSet<>();
/*
* Add SimpleGrantedAuthority to set as appropriate
*/
user = new org.springframework.security.core.userdetails.User(username, optional.get().getPassword(), set);
} catch (UsernameNotFoundException exception) {
throw exception;
} catch (Exception exception) {
throw new UsernameNotFoundException(username);
}
return user;
}
}
and wire it in with:
#Autowired private UserDetailsService userDetailsService = null;
... private PasswordEncoder passwordEncoder = ...;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
For some additional clarity, here is the complete context of my implementation:
#Service
#NoArgsConstructor #ToString #Log4j2
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired private CredentialRepository credentialRepository = null;
#Autowired private AuthorityRepository authorityRepository = null;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = null;
try {
Optional<Credential> credential = credentialRepository.findById(username);
Optional<Authority> authority = authorityRepository.findById(username);
HashSet<GrantedAuthority> set = new HashSet<>();
if (authority.isPresent()) {
authority.get().getGrants().stream()
.map(Authorities::name)
.map(SimpleGrantedAuthority::new)
.forEach(set::add);
}
user = new User(username, credential.get().getPassword(), set);
} catch (UsernameNotFoundException exception) {
throw exception;
} catch (Exception exception) {
throw new UsernameNotFoundException(username);
}
return user;
}
}
suppose db table name is users and authorities. dataSource is configured in application.yml.
#Autowired
private DataSource dataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select username,password,enabled from users WHERE username=?")
.authoritiesByUsernameQuery("select username,authority from authorities where username=?")
.passwordEncoder(new BCryptPasswordEncoder());
}
}
Using postman, I can get a list of users with a get request to: http://localhost:8080/users.
But when I send a post request to the same address, I get a 403 error.
#RestController
public class UserResource {
#Autowired
private UserRepository userRepository;
#GetMapping("/users")
public List<User> retrievaAllUsers() {
return userRepository.findAll();
}
#PostMapping("/users")
public ResponseEntity<Object> createUser(#RequestBody User user) {
User savedUser = userRepository.save(user);
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(savedUser.getId())
.toUri();
return ResponseEntity.created(location).build();
}
}
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
/*#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(new BCryptPasswordEncoder());
}*/
/*#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests()
.antMatchers("/users/**").hasRole("ADMIN")
.and().csrf().disable().headers().frameOptions().disable();
}*/
}
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue
private Long id;
private String name;
private String password;
#Enumerated(EnumType.STRING)
private Role role;
// TODO which cna be removed
public User() {
super();
}
public User(Long id, String name, String password, Role role) {
this.id = id;
this.name = name;
this.password = password;
this.role = role;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
}
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
INSERT INTO user VALUES (1, 'user1', 'pass1', 'ADMIN');
INSERT INTO user VALUES (2, 'user2', 'pass2', 'USER');
INSERT INTO user VALUES (3,'user3', 'pass3', 'ADMIN')
EDIT
EDit 2
added delete, but it also gives a 403?
#DeleteMapping("/users/{id}")
public void deleteUser(#PathVariable long id) {
userRepository.deleteById(id);
}
edit 4
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/users/**").permitAll();
}
}
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#EnableWebSecurity enables spring security and it by default enables csrf support, you must disable it in order to prevent 403 errors.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
Or send csrf token with each request.
Note: disabling csrf makes application less secure, best thing to do is send csrf token.
When you use spring boot with spring security and if you are accessing your API's(POST, PUT, DELETE) from Postman or something, they wont be accessible and error is related to authorization like forbidden 403.
So in that case, you have to disabled to csrf functionality to run and test the API from Postman.
The answer provided by #benjamin c is right. You have to add the class with the this configuration will work.
Make sure you are removing this when you add your code in production. CSRF protection is must and you have to keep it in security functionality.
I am just extending his answer for more details by providing complete class details. My requirement was to just test the API from Postman, so I added this class, and able to test the API from Postman.
But after that I have added Spring Junit classes to test my functionalities and removed this class.
#Configuration
#EnableWebSecurity
public class AppWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().permitAll();
}
}
Hope this helps to someone.
403 means you don't have authorization. Even though you commented out your method, your code will still be preconfigured with default security access.
You can add:
http.authorizeRequests()
.antMatchers("/users/**").permitAll();
UPDATE : The configuration with csrf disabled:
http.csrf()
.ignoringAntMatchers("/users/**")
.and()
.authorizeRequests()
.antMatchers("/users/**").permitAll();
This configuration in SecurityConfig class helped me solve it:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
Please configure your http like this ;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
//configureothers if u wants.
.csrf().disable();
}
Please read for more CSRF
I'm learning Spring and now trying to configure security. I need to create secured endpoints and one not secured for registration.
But when I'm trying to access http://localhost:8080/register I'm getting error "An Authentication object was not found in the SecurityContext" and "AuthenticationCredentialsNotFoundException".
I have repeated example in docs but still get this error.
WebSecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/register").permitAll();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
OAuth2Config.java
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("userDetailsService")
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
#Value("3600")
private int expiration;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer configurer) throws Exception {
configurer.authenticationManager(authenticationManager);
configurer.userDetailsService(userDetailsService);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("app").secret("secret").accessTokenValiditySeconds(expiration)
.scopes("read", "write").authorizedGrantTypes("password", "refresh_token").resourceIds("resource");
}
}
RegisterController.java
#RestController
public class RegisterController {
#Autowired
UserDao userDao;
#Autowired
CityDao cityDao;
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity<?> addUser(#RequestParam(value = "email") String email, #RequestParam(value = "firstName") String firstName,
#RequestParam(value = "lastName") String lastName, #RequestParam(value = "city") Long cityId,
#RequestParam(value = "password") String password){
User userToFind = userDao.findByEmail(email);
City city = cityDao.findById(cityId).get();
if (userToFind != null) {
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
User user = new User(firstName, lastName, email, city, passwordEncoder.encode(password));
return new ResponseEntity<>(userDao.save(user), HttpStatus.CREATED);
}
else{
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
}
}
I'm implementing a project using Spring security oauth2, everything works perfectly, now I want to start digging deeper beyond the basics. I want to check if the user making the request is the actual user owner of the resource, the end result would be for example:
/private/users/{uuid}/clients returns all clients for the specified user.
So my controller now looks like this:
#RestController
public class HomeController {
#Autowired
private UserService userService;
#GetMapping(value = "/")
public String index() {
return "Hello world";
}
#GetMapping(value = "/private")
public String privateTest(Principal principal) {
User user = userService.get(principal.getName());
return user.getUuid();
}
}
EDIT: The full security code (working) for a better explanation.
ResourceServerConfig
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable().and()
.authorizeRequests()
.antMatchers("/","/home","/register","/login").permitAll()
.antMatchers("/private/**").authenticated();
}
}
CustomUserDetails with getters and setters off course
public class CustomUserDetails implements UserDetails {
private Collection<? extends GrantedAuthority> authorities;
private String password;
private String username;
private String uuid;
public CustomUserDetails(User user) {
this.username = user.getUsername();
this.password = user.getPassword();
this.uuid = user.getUuid();
this.authorities = translate(user.getRoles());
}
}
AuthorizationServerConfig
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("my-trusted-client")
.authorizedGrantTypes("client_credentials", "password")
.authorities("ROLE_CLIENT","ROLE_TRUSTED_CLIENT").scopes("read","write","trust")
.resourceIds("oauth2-resource").accessTokenValiditySeconds(5000).secret("secret");
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("isAuthenticated()");
}
}
Main
#SpringBootApplication
public class DummyOauthApplication {
#Autowired
private PasswordEncoder passwordEncoder;
public static void main(String[] args) {
SpringApplication.run(DummyOauthApplication.class, args);
}
#Autowired
public void authenticationManager(AuthenticationManagerBuilder builder, UserRepository repository, UserService service) throws Exception {
//Setup a default user if db is empty
if (repository.count() == 0) {
service.save(new User("user", "password", UUID.randomUUID().toString(), Arrays.asList(new Role("USER"), new Role("ACTUATOR"))));
}
builder.userDetailsService(userDetailsService(repository)).passwordEncoder(passwordEncoder);
}
private UserDetailsService userDetailsService(final UserRepository repository) {
return username -> new CustomUserDetails(repository.findByUsername(username));
}
}
So, using the way I've implemented. I can get the actual user but it implies a database query every time an endpoint is called. Getting the user and match with the user uuid.
I want to find another way that I can get the user and then compare if the uuid = user.getUuid()
Thanks in advance.
After some time and a lot of mistakes, I've managed to find a solution that I leave here. The CustomUserDetails can be seen in the question and from there you can easily get the uuid and match with the requested one.
public static CustomUserDetails getCurrentUser() {
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
if (authentication != null) {
if (authentication.getPrincipal() instanceof CustomUserDetails) {
return (CustomUserDetails) authentication.getPrincipal();
}
}
throw new IllegalStateException("User not found!");
}
EDIT: if you want to return the user you do something like this
public class CustomUserDetails implements UserDetails {
private Collection<? extends GrantedAuthority> authorities;
private String password;
private String username;
private User user;
public CustomUserDetails(User user) {
this.username = user.getUsername();
this.password = user.getPassword();
this.user = user;
this.authorities = translate(user.getRoles());
}
}
And then in a Utils or something,
public static User getCurrentUser() {
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
if (authentication != null) {
if (authentication.getPrincipal() instanceof CustomUserDetails) {
CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
return userDetails.getUser();
}
}
throw new IllegalStateException("User not found!");
}
Thanks for all the effort.
Perhaps you could implement a custom AuthenticationProvider and store user details as Principal
Spring Security Authentication Provider