Oauth 2: Spring Boot 2: Auth Server /oauth/check_token returns user_name as null - spring-boot

I have implemented 2 distinct servers: auth server and a resource server using Spring Boot 2.1.6.RELEASE and spring-cloud-starter-oauth2 version Greenwich.RELEASE
I am able to successfully get an access_token from auth server, use it to access protected api on resource server.
However I am not able to get user_name in the response returned by auth server's /oauth/check_token endpoint I can confirm that user_name is present in user table.
curl http://localhost:5000/oauth/check_token?token=a3ee84ee-6d3a-4a8f-af19-5446b55c637f | jq .
returns following:
{
"aud": [
"article"
],
"user_name": null,
"scope": [
"READ",
"WRITE",
"UPDATE",
"DELETE"
],
"active": true,
"exp": 1563849438,
"authorities": [
"ROLE_administrator",
"create_article",
"read_article",
"delete_article",
"update_article"
],
"client_id": "myclient"
}
AuthorizationServerConfiguration
#Configuration
public class AuthorizationServerConfiguration implements AuthorizationServerConfigurer {
private PasswordEncoder passwordEncoder;
private DataSource dataSource;
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
public AuthorizationServerConfiguration(
PasswordEncoder passwordEncoder,
DataSource dataSource,
AuthenticationManager authenticationManager) {
this.passwordEncoder = passwordEncoder;
this.dataSource = dataSource;
this.authenticationManager = authenticationManager;
}
#Bean
TokenStore jdbcTokenStore() {
return new JdbcTokenStore(dataSource);
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
return new JwtAccessTokenConverter();
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
// security.checkTokenAccess("isAuthenticated()").tokenKeyAccess("permitAll()");
security.checkTokenAccess("permitAll()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource).passwordEncoder(passwordEncoder);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenStore(jdbcTokenStore());
endpoints.authenticationManager(authenticationManager);
//TODO JWT
// endpoints.accessTokenConverter(accessTokenConverter());
}
}
WebSecurityConfiguration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/version").permitAll()
.antMatchers("/api/**").authenticated();
}
}
UserDetailsServiceImpl
#Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
private UserRepository userRepository;
#Autowired
public UserDetailsServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
Optional<User> optionalUser = userRepository.findByUserName(userName);
optionalUser.orElseThrow(() -> new UsernameNotFoundException("Username or password wrong"));
UserDetails userDetails = new AuthUserDetail(optionalUser.get());
new AccountStatusUserDetailsChecker().check(userDetails);
return userDetails;
}
}
Main application class
#SpringBootApplication
#EnableAuthorizationServer
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
AuthUserDetail
package com.myapplication.models;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class AuthUserDetail extends User implements UserDetails {
public AuthUserDetail(User user) {
super(user);
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
getRoles().forEach(role -> {
grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
role.getPermissions().forEach(permission -> {
grantedAuthorities.add(new SimpleGrantedAuthority(permission.getName()));
});
});
return grantedAuthorities;
}
#Override
public String getPassword() {
return super.getPassword();
}
#Override
public String getUsername() {
return super.getUserName();
}
#Override
public boolean isAccountNonExpired() {
return super.isAccountNonExpired();
}
#Override
public boolean isAccountNonLocked() {
return super.isAccountNonLocked();
}
#Override
public boolean isCredentialsNonExpired() {
return super.isCredentialsNonExpired();
}
#Override
public boolean isEnabled() {
return super.isEnabled();
}
}
User
package com.myapplication.models;
import lombok.Data;
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name = "user")
#Data
public class User {
public User() {
}
public User(User user) {
this.userName = user.getUserName();
this.password = user.getPassword();
this.email = user.getEmail();
this.enabled = user.isEnabled();
this.accountNonExpired = user.isAccountNonExpired();
this.credentialsNonExpired = user.isCredentialsNonExpired();
this.accountNonLocked = user.isAccountNonLocked();
this.roles = user.getRoles();
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "user_name")
private String userName;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
#Column(name = "enabled")
private boolean enabled;
#Column(name = "account_non_expired")
private boolean accountNonExpired;
#Column(name = "credentials_non_expired")
private boolean credentialsNonExpired;
#Column(name = "account_non_locked")
private boolean accountNonLocked;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "role_user", joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {
#JoinColumn(name = "role_id", referencedColumnName = "id")})
private List<Role> roles;
}
user table ddl
create table if not exists user
(
id int auto_increment
primary key,
user_name varchar(100) not null,
password varchar(1024) not null,
email varchar(1024) not null,
enabled tinyint not null,
account_non_expired tinyint not null,
credentials_non_expired tinyint not null,
account_non_locked tinyint not null,
constraint user_name
unique (user_name)
);

I got the same problem after I modified column names of my user table from camelCase to underscore_case.
To resolve this I made sure the user, permission and role class is implementing Serializable class
#Entity
#Table(name = "user")
public class User implements Serializable {
----
----
}
and also remove the following spring jpa property if added
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

Related

spring security - 403 forbidden and not call loadUserByUsername()

I added spring security to my app. I would like to secure any endpoints, and open "/users" for all
User configuration:
#Entity
#Table(name = "users")
#AllArgsConstructor
#NoArgsConstructor
public class UserEntity implements UserDetails {
#Id
private String id;
private String login;
private String password;
private String role;
public UserEntity(UserCreateRequestModel userCreateRequestModel) {
this.id = UUID.randomUUID().toString();
this.login = userCreateRequestModel.getLogin();
this.password = PasswordService.codePassword(userCreateRequestModel.getPassword());
this.role = "USER";
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> list = new ArrayList<>();
list.add(new SimpleGrantedAuthority(role));
return new ArrayList<SimpleGrantedAuthority>();
}
#Override
public String getUsername() {
return login;
}
#Override
public String getPassword() {
return password;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
My custom WebSecurityConfigurerAdapter:
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
private final MyUserDetailsService myUserDetailsService;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/users", "/users/**").permitAll()
.anyRequest().hasAuthority("USER")
;
}
#Override
protected UserDetailsService userDetailsService() {
return myUserDetailsService;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
and next I added custom UserDetailsService
#Service
#RequiredArgsConstructor
public class MyUserDetailsService implements UserDetailsService {
private final UserEntityRepository userEntityRepository;
#Override
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
System.out.println(userEntityRepository.findByLogin(login));
return userEntityRepository.findByLogin(login).orElseThrow((() -> new ObjectNotFoundException(login)));
}
and now I am getting problem. Endpoint for "/users" is open to all, I can send request with any problem. But when I am trying for example any #PostMapping on endpoint "/shapes"
I am getting 403 status response. In postman I think everything is OK:
of course user exists in database. In UserEntity user isEnabled, isAccountNonExpired, isAccountNonLocked and isCredentialsNonExpired - everything is on true.
In your getAuthorities() method of UserEntity class you're returning an empty List, so spring-security sees no authorities (roles) for an authenticated user.
Change this method like this:
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singletonList(new SimpleGrantedAuthority(role));
}

#AuthenticationPrincipal returns null

I setup my Spring Security application according to the reference document and after hours of troubleshooting I continue to get a null #AuthenticationPrincipal passed into my controller.
The authentication mechanism is working fine against the users in my database but still a null #AuthenticationPrincipal. I consulted several internet posts but still I am getting null.
WebSecurityConfig:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserService userService;
#Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider provider(){
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(passwordEncoder());
provider.setUserDetailsService(userService);
return provider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/registration").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(provider());
}
}
Message (entity):
#Entity
#Table(name = "sweater_message")
public class Message {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String text;
private String tag;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User author;
public Message(String text, String tag, User user) {
this.author = user;
this.text = text;
this.tag = tag;
}
public Message() {
}
...getters and setters
User(entity):
#Entity
#Table(name = "sweater_user")
public class User implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private boolean active;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(
name = "sweater_user_role",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id")
)
private Collection<Role> roles;
public User(String username, String password, boolean active, Collection<Role> roles) {
this.username = username;
this.password = password;
this.active = active;
this.roles = roles;
}
public User() {
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return isActive();
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return getRoles().stream().map(role -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toList());
...getters and setters
}
UserService
#Service
public class UserService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
public User save(User user) {
User saveUser = new User(
user.getUsername(),
new BCryptPasswordEncoder().encode(user.getPassword()),
true,
Arrays.asList(new Role("USER")));
return userRepository.save(saveUser);
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User findUser = userRepository.findByUsername(username);
if (findUser == null) {
throw new UsernameNotFoundException("There is no user with this username");
}
return new org.springframework.security.core.userdetails.User(
findUser.getUsername(),
findUser.getPassword(),
mapRolesToAuthorities(findUser.getRoles()));
}
public Collection<? extends GrantedAuthority> mapRolesToAuthorities(Collection<Role> roles) {
return roles.stream().map(role -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toSet());
}
}
Controller:
#PostMapping("/main")
public String add(
#AuthenticationPrincipal User user,
#RequestParam String text,
#RequestParam String tag,
Map<String, Object> model
){
...user is null
}
try Changing #AuthenticationPrincipal User user to #AuthenticationPrincipal UserDetails userDetails since loadUserByUsername returns UserDetails
Using SecurityContextHolder.getContext().getAuthentication().getPrincipal() in your controller to see whether your userdetails object is stored in the right place since #AuthenticationPrincipal is an abbreviation for (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal().
For example:
#GetMapping("/all")
public ResponseEntity<String> test(#AuthenticationPrincipal AuthUserDetails userDetails) {
System.out.println(SecurityContextHolder.getContext().getAuthentication().getPrincipal());
return ResponseEntity.ok("success");
}
If the out print is something other than the UserDetails object, it means that you did not set Principal correctly when you are initializing your Authentication in the filter class. Let's use UsernamePasswordAuthenticationToken as an example:
// in filter class
#Component
public class JwtFilter extends OncePerRequestFilter {
private JwtProvider jwtProvider;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Optional<AuthUserDetails> authUserDetailOptional = jwtProvider.resolveToken(request); // extract jwt from request, generate a userdetails object
if (authUserDetailOptional.isPresent()){
AuthUserDetails authUserDetails = authUserDetailOptional.get();
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
authUserDetails, // set your authUserDetails here!!
null,
authUserDetails.getAuthorities()
); // generate authentication object
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
} else {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "The token is not valid.");
}
}

How get more information for user spring boot

hello everyone I hope you're well I've been using spring boot for 7 months but now I'm in front of a wall for 1 week with oauth 2 all working as login and registration but this during when I connect the user information is not enough provided
here is the result I get
{
"exp": 1610236389,
"user_name": "fiasco555",
"authorities": [
"ROLE_USER"
],
"jti": "1JQTeD5wuRG7vDkIKKg4XUgohZw",
"client_id": "clientId",
"scope": [
"read",
"write"
]
I would like more information about my users here is a copy of the code
AuthorizationServerConfiguration.java
import javax.sql.DataSource;
import java.security.KeyPair;
#Configuration
#EnableAuthorizationServer
#EnableConfigurationProperties(SecurityProperties.class)
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private final DataSource dataSource;
#Autowired
private final PasswordEncoder passwordEncoder;
#Autowired
private final AuthenticationManager authenticationManager;
#Autowired
private final SecurityProperties securityProperties;
#Autowired
private final UserDetailsService userDetailsService;
#Autowired
private MyUserDetailsService myUserDetailsService;
private JwtAccessTokenConverter jwtAccessTokenConverter;
private TokenStore tokenStore;
public AuthorizationServerConfiguration(final DataSource dataSource, final PasswordEncoder passwordEncoder,
final AuthenticationManager authenticationManager, final SecurityProperties securityProperties,
final UserDetailsService userDetailsService) {
this.dataSource = dataSource;
this.passwordEncoder = passwordEncoder;
this.authenticationManager = authenticationManager;
this.securityProperties = securityProperties;
this.userDetailsService = userDetailsService;
}
#Bean
public TokenStore tokenStore() {
if (tokenStore == null) {
tokenStore = new JwtTokenStore(jwtAccessTokenConverter());
}
return tokenStore;
}
#Bean
public DefaultTokenServices tokenServices(final TokenStore tokenStore,
final ClientDetailsService clientDetailsService) {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(tokenStore);
tokenServices.setClientDetailsService(clientDetailsService);
tokenServices.setAuthenticationManager(this.authenticationManager);
return tokenServices;
}
#Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
if (jwtAccessTokenConverter != null) {
return jwtAccessTokenConverter;
}
SecurityProperties.JwtProperties jwtProperties = securityProperties.getJwt();
System.out.println("YESS" + jwtProperties.getKeyPairAlias());
KeyPair keyPair = keyPair(jwtProperties, keyStoreKeyFactory(jwtProperties));
jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setKeyPair(keyPair);
return jwtAccessTokenConverter;
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(this.dataSource);
}
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(this.authenticationManager)
.accessTokenConverter(jwtAccessTokenConverter())
.userDetailsService(this.userDetailsService)
.tokenStore(tokenStore());
}
#Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer.passwordEncoder(this.passwordEncoder).tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
private KeyPair keyPair(SecurityProperties.JwtProperties jwtProperties, KeyStoreKeyFactory keyStoreKeyFactory) {
return keyStoreKeyFactory.getKeyPair(jwtProperties.getKeyPairAlias(), jwtProperties.getKeyPairPassword().toCharArray());
}
#Deprecated
private KeyStoreKeyFactory keyStoreKeyFactory(SecurityProperties.JwtProperties jwtProperties) {
return new KeyStoreKeyFactory(jwtProperties.getKeyStore(), jwtProperties.getKeyStorePassword().toCharArray());
}
}
ResourceServerConfiguration.java
import static java.nio.charset.StandardCharsets.UTF_8;
#Configuration
#EnableResourceServer
#EnableConfigurationProperties(SecurityProperties.class)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String ROOT_PATTERN = "/**";
private final SecurityProperties securityProperties;
private TokenStore tokenStore;
public ResourceServerConfiguration(final SecurityProperties securityProperties) {
this.securityProperties = securityProperties;
}
#Override
public void configure(final ResourceServerSecurityConfigurer resources) {
resources.tokenStore(tokenStore());
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST,"/register/**").permitAll()
.antMatchers(HttpMethod.GET, ROOT_PATTERN).access("#oauth2.hasScope('read')")
.antMatchers(HttpMethod.POST, ROOT_PATTERN).access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.PATCH, ROOT_PATTERN).access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.PUT, ROOT_PATTERN).access("#oauth2.hasScope('write')")
.antMatchers(HttpMethod.DELETE, ROOT_PATTERN).access("#oauth2.hasScope('write')");
}
#Bean
public DefaultTokenServices tokenServices(final TokenStore tokenStore) {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore);
return tokenServices;
}
#Bean
public TokenStore tokenStore() {
if (tokenStore == null) {
tokenStore = new JwtTokenStore(AccessJwtAccessTokenConverter());
}
return tokenStore;
}
#Bean
public JwtAccessTokenConverter AccessJwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setVerifierKey(getPublicKeyAsString());
// converter.setSigningKey(());
return converter;
}
private String getPublicKeyAsString() {
try {
return IOUtils.toString(securityProperties.getJwt().getPublicKey().getInputStream(), UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
SecurityProperties.java
#ConfigurationProperties("security")
public class SecurityProperties {
private JwtProperties jwt;
public JwtProperties getJwt() {
return jwt;
}
public void setJwt(JwtProperties jwt) {
this.jwt = jwt;
}
public static class JwtProperties {
private Resource keyStore;
private String keyStorePassword;
private String keyPairAlias;
private String keyPairPassword;
private Resource publicKey;
public Resource getKeyStore() {
return keyStore;
}
public void setKeyStore(Resource keyStore) {
this.keyStore = keyStore;
}
public String getKeyStorePassword() {
return keyStorePassword;
}
public void setKeyStorePassword(String keyStorePassword) {
this.keyStorePassword = keyStorePassword;
}
public String getKeyPairAlias() {
return keyPairAlias;
}
public void setKeyPairAlias(String keyPairAlias) {
this.keyPairAlias = keyPairAlias;
}
public String getKeyPairPassword() {
return keyPairPassword;
}
public void setKeyPairPassword(String keyPairPassword) {
this.keyPairPassword = keyPairPassword;
}
public Resource getPublicKey() {
return publicKey;
}
public void setPublicKey(Resource publicKey) {
this.publicKey = publicKey;
}
}
}
WebSecurityConfiguration.java
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableConfigurationProperties(value= DataSourceProperties.class)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final DataSource dataSource;
private PasswordEncoder passwordEncoder;
private UserDetailsService userDetailsService;
public WebSecurityConfiguration(final DataSource dataSource) {
this.dataSource = dataSource;
}
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
if (passwordEncoder == null) {
passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
return passwordEncoder;
}
#Bean
public UserDetailsService userDetailsService() {
JdbcDaoImpl jdbcDaoImpl = new JdbcDaoImpl();
if (userDetailsService == null) {
userDetailsService = new JdbcDaoImpl();
((JdbcDaoImpl) userDetailsService).setDataSource(dataSource);
}
return userDetailsService;
}
}
Models/User.java
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "username", unique = true)
#NotEmpty(message = "Ce champs doit être remplie")
#Size(min = 6)
private String username;
#Column(name = "fullname")
#NotEmpty(message = "Ce champs doit être remplie")
#Size(min = 6)
private String fullname;
#Column(name = "sexe")
private String sexe;
#Column(name = "uuid")
private String uuid = UUID.randomUUID().toString();
#Column(name = "tel", unique = true)
#NotEmpty(message = "Ce champ doit être remplie")
#Pattern(regexp = "^7[7860][0-9]{7}$", message = "Ce Format de n'est pas valide")
private String tel;
#Column(name = "email", unique = true)
#Email(message = "Ce adresse email n'est pas valide")
private String email;
#Column(name = "password")
#NotEmpty(message = "Ce champ doit être remplie")
private String password;
#Column(name = "register_at")
Date dateRegister = new Date(new Date().getTime());
#Column(name = "enabled")
private Boolean enabled = true;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSexe() {
return sexe;
}
public void setSexe(String sexe) {
this.sexe = sexe;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getDateRegister() {
return dateRegister;
}
public void setDateRegister(Date dateRegister) {
this.dateRegister = dateRegister;
}
public Boolean getEnabled() {
return enabled;
}
public String getFullname() {
return fullname;
}
public void setFullname(String fullname) {
this.fullname = fullname;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
}
you know my problem so propose me some solutions I'm listening here to the original code Here (on my side I've made some modification you can compare it)
The key point of your problem lies in the JwtAccessTokenConverter configuration in AuthorizationServerConfiguration. Its convertAccessToken() method is responsible for converting the authentication information into JWT.
The convertAccessToken() method is actually executed by the tokenConverter property whose default value is DefaultAccessTokenConverter
public class JwtAccessTokenConverter {
private AccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
#Override
public Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
return tokenConverter.convertAccessToken(token, authentication);
}
//...
}
DefaultAccessTokenConverter calls the userTokenConverter.convertUserAuthentication() to convert Authentication to JWT attribute
public class DefaultAccessTokenConverter {
private UserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter();
public Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
Map<String, Object> response = new HashMap<String, Object>();
//...
response.putAll(userTokenConverter.convertUserAuthentication(authentication.getUserAuthentication()));
//...
return resposne;
}
}
DefaultUserAuthenticationConverter will convert username and authorities into JWT attributes which is the real reason that only user_name and authorities can find in your jwt token.
public class DefaultUserAuthenticationConverter {
public Map<String, ?> convertUserAuthentication(Authentication authentication) {
Map<String, Object> response = new LinkedHashMap<String, Object>();
response.put(USERNAME, authentication.getName());
if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
}
return response;
}
}
Now everything is clear, create your own UserAuthenticationConverter implementation and configure it in AuthorizationServerConfiguration. this is an implementation of mine for your reference
public class SubjectAttributeUserTokenConverter extends DefaultUserAuthenticationConverter {
#Override
public Map<String, ?> convertUserAuthentication(Authentication authentication) {
User user = (User) authentication.getPrincipal();
Map<String, Object> response = new LinkedHashMap<>();
response.put("name", authentication.getName());
ObjectMapper objectMapper = new ObjectMapper();
try {
Map<String, ?> map = objectMapper.readValue(objectMapper.writeValueAsString(user), Map.class);
response.putAll(map);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
if (authentication.getAuthorities() != null) {
response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
}
return response;
}
}
#EnableAuthorizationServer
#Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setKeyPair(keyPair());
DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
accessTokenConverter.setUserTokenConverter(new SubjectAttributeUserTokenConverter());
converter.setAccessTokenConverter(accessTokenConverter);
return converter;
}
}
By the way, spring security oauth2 project is in maintenance mode. Most of its functions such as Oauth2 ResourceServer and Oauth2 Client have been implemented in spring security that is preferable. For more details, please refer to OAuth 2.0 Features Matrix

Spring Starter Security not authenticating

When I add a User with "ROLE_USER" permissions, I am unable to authenticate. 401s are returned consistently when attempting to authenticate with username: "username" and password: "password".
I can see in the JSON that's output that the BCryptPasswordEncoder is encoding passwords as it should be, but regardless of whether I use the original password or encoded version, I'm still unable to authenticate.
I've been working on this for a couple of days to no avail. Is there anything I'm missing?
Code is below --
DatabaseLoader:
User user = new User("first", "last", "username", "password", "email", "phone", new String[] {"ROLE_USER"});
userRepository.save(user);
DetailsService:
#Component
public class DetailsService implements UserDetailsService {
#Autowired
UserRepository users;
#Override
public UserDetails loadUserByUsername(String userUsername) throws UsernameNotFoundException {
User user = users.findByUsername(userUsername);
if (user == null) {
throw new UsernameNotFoundException(userUsername + " was not found");
}
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getUserPassword(),
AuthorityUtils.createAuthorityList(user.getUserRoles())
);
}
}
WebSecurityConfig:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
DetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(User.PASSWORD_ENCODER);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable();
}
}
User:
#Entity
public class User {
public static final PasswordEncoder PASSWORD_ENCODER = new BCryptPasswordEncoder();
private long userId;
private String userFirstName;
private String userLastName;
private String username;
#JsonIgnore
private String userPassword;
private String userPhone;
private String userEmail;
#JsonIgnore
private String[] userRoles;
public User() {}
public User(String userFirstName, String userLastName, String username, String userPassword, String userPhone, String userEmail, String[] userRoles) {
this.userFirstName = userFirstName;
this.userLastName = userLastName;
this.username = username;
setUserPassword(userPassword);
this.userPhone = userPhone;
this.userEmail = userEmail;
this.userRoles = userRoles;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
#Column
public String getUserFirstName() {
return userFirstName;
}
public void setUserFirstName(String userFirstName) {
this.userFirstName = userFirstName;
}
#Column
public String getUserLastName() {
return userLastName;
}
public void setUserLastName(String userLastName) {
this.userLastName = userLastName;
}
#Column
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#Column
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = PASSWORD_ENCODER.encode(userPassword);
}
#Column
public String getUserPhone() {
return userPhone;
}
public void setUserPhone(String userPhone) {
this.userPhone = userPhone;
}
#Column
public String getUserEmail() {
return userEmail;
}
public void setUserEmail(String userEmail) {
this.userEmail = userEmail;
}
#Column
public String[] getUserRoles() {
return userRoles;
}
public void setUserRoles(String[] userRoles) {
this.userRoles = userRoles;
}
}
Your question isn't very clear about problem. But i guess you are stuck in user authentication with spring starter security.
You should check this question

hasRole always return 403

I can't seem to get my security configuration right. No matter what I do when using hasRole my endpoints always return 403.
Also I can't get anything to work unless I duplicate my antMatchers under both .requestMatchers() and .authorizeRequests(). I'm clearly missing something here.
Basically I want everything to require authentication but a few endpoints only to be accessable if the user is member of certain groups (for now just admin).
My security configuration is as follows. Everything beside hasRole works.
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.requestMatchers()
.antMatchers(HttpMethod.GET, "/v2/api-docs", "/swagger-resources/**", "/swagger-ui.html")
.antMatchers(HttpMethod.GET, "/users")
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/v2/api-docs", "/swagger-resources/**", "/swagger-ui.html").permitAll()
.antMatchers(HttpMethod.GET, "/users").hasRole("ADMIN")
.anyRequest().authenticated();
}
// Inspiration: https://spring.io/blog/2015/06/08/cors-support-in-spring-framework#comment-2416096114
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**");
}
}
My AuthenticationConfiguration is as follows
#Configuration
#EnableResourceServer
public class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
private final UserDetailsService userService;
private final PasswordEncoder passwordEncoder;
public AuthenticationConfiguration(UserDetailsService userService, PasswordEncoder passwordEncoder) {
this.userService = userService;
this.passwordEncoder = passwordEncoder;
}
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userService)
.passwordEncoder(passwordEncoder);
}
}
My AuthorizationServerConfiguration is as follows
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
public AuthorizationServerConfiguration(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("html5")
.secret("password")
.authorizedGrantTypes("password")
.scopes("openid");
}
}
I'll happily post my user service and other stuff. But everything seems to work beside hasRole and Principal is loaded with the right authorities (roles). But please let me know if I should post any more code.
The entire source code can be found here.
Have you tried with "ROLE_ADMIN" rather than just "ADMIN"? Take a look at this for reference:
Spring security added prefix "ROLE_" to all roles name?
Following up on my comments to the question I'll provide sample OAuth2 Configuration classes I've used for testing. I always use two different webapps, because I want a clear line between auth server and resource server(and because it makes configurations so much harder....), so my example probably needs some adjustments when used in a single webapp.
Configuration for the auth server:
#EnableAuthorizationServer
#Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
private TokenStore tokenStore;
private DataSource dataSource;
#Autowired
public OAuth2Config(TokenStore tokenStore,
DataSource dataSource) {
this.tokenStore = tokenStore;
this.dataSource = dataSource;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore);
}
#Configuration
public static class TokenStoreConfiguration {
#Bean
public TokenStore tokenStore(DataSource dataSource) {
return new JdbcTokenStore(dataSource);
}
}
}
Configuration for resource server:
#EnableResourceServer
#Configuration
public class OAuth2Config extends ResourceServerConfigurerAdapter {
public static final String PROPERTY_RESOURCE_ID = "com.test.oauth.resourceId";
private Environment environment;
private TokenStore tokenStore;
#Autowired
public OAuth2Config(Environment environment,
TokenStore tokenStore) {
this.environment = environment;
this.tokenStore = tokenStore;
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore)
.resourceId(environment.getProperty(PROPERTY_RESOURCE_ID))
.stateless(true);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/demo")
.access("hasRole('DEMO')")
.anyRequest().denyAll()
.and()
.formLogin().disable()
.logout().disable()
.jee().disable()
.x509().disable();
}
#Configuration
public static class TokenStoreConfiguration {
#Bean
public TokenStore tokenStore(DataSource dataSource) {
return new JdbcTokenStore(dataSource);
}
}
}
Obviously this requires that you have a DataSource bean configured. This implementation uses the default tables as provided by spring security OAuth2(they are far from ideal, but can be customized if required).
There are a few things you might want to adjust for your case(I'll leave the classes I provided as is for a reference if people might want to use it with JDBC):
Create only one bean of type TokenStore and use InMemoryTokenStore instead of JdbcTokenStore
replace the configuration for clients with your inMemory() implementation and remove all references to my autowired DataSource
Provide requestMatchers() before specifying authorizeRequests() in your resource server configuration. Depending on the order the configuration is processed and the filter chains are added this might be required to allow the oauth endpoints to be reached without requiring an OAuth token.
Edit: Seeing the answer by ritesh.garg I think that what I provided might not resolve your issues, but might help some figuring out where and how to start configuring Spring Security OAuth2(When I did it the first time I found it hard to do, because back then I couldn't find any clear examples, though this might have changed)
I had the same problem, I just forgot to implements getAuthorities() method from UserDetails (SpringSecurity class). Look my entity:
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
#Entity
#Table(name = "tb_user")
public class User implements UserDetails, Serializable {
private static final long serialVersionUID = -6519124777839966091L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
#Column(unique = true)
private String email;
private String password;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "tb_user_role",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id")
)
private Set<Role> roles = new HashSet<>();
public User() {
}
public User(Long id, String firstName, String lastName, String email, String password) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.password = password;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public void setPassword(String password) {
this.password = password;
}
public Set<Role> getRoles() {
return roles;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles.stream().map(role -> new SimpleGrantedAuthority(role.getAuthority()))
.collect(Collectors.toList());
}
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return email;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id);
}
#Override
public int hashCode() {
return Objects.hash(id);
}
}
The method getAuthorities return null by default when you extends UserDetails class from security package, you need implement something like that:
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles.stream().map(role -> new SimpleGrantedAuthority(role.getAuthority()))
.collect(Collectors.toList());
}
I hope this help someone, sorry about my english errors! hehe

Resources