#AuthenticationPrincipal returns null - spring

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.");
}
}

Related

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

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

Spring security authentication fails with Custom user

I am trying to authenticate with Custom User that implements UserDetails. Here is my custome User class. (This class is also extended by other classes like Citizen and Employee as well).
#Entity
#Table(name = "user")
#Inheritance(strategy = InheritanceType.JOINED)
public class User implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Integer id;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
#Column(name = "phone")
private String phone;
#Column(name = "address")
private String address;
#Column(name = "status")
private boolean isActive;
#CreationTimestamp
#Column(name = "created_at")
private LocalDate createdAt;
#UpdateTimestamp
#Column(name = "updated_at")
private LocalDate updatedAt;
#Transient
private Set<GrantedAuthority> authorityList;
#ManyToMany
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id"))
private Set<Role> roles;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Override
public String getUsername() {
return username;
}
#Override
public boolean isAccountNonExpired() {
return false;
}
#Override
public boolean isAccountNonLocked() {
return false;
}
#Override
public boolean isCredentialsNonExpired() {
return false;
}
#Override
public boolean isEnabled() {
return false;
}
public void setUsername(String username) {
this.username = username;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorityList;
}
#Override
public String getPassword() {
return password;
}
public void setAuthorityList(Set<GrantedAuthority> authorityList) {
this.authorityList = authorityList;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public boolean isActive() {
return isActive;
}
public void setActive(boolean active) {
isActive = active;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
public LocalDate getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDate createdAt) {
this.createdAt = createdAt;
}
public LocalDate getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDate updatedAt) {
this.updatedAt = updatedAt;
}
}
Also I have implemented UserDetailsService as
#Service
public class UserDetailsServiceImpl implements UserDetailsService{
#Autowired
private UserRepository userRepository;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
for (Role role : user.getRoles()){
grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
}
user.setAuthorityList(grantedAuthorities);
return user;// new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities);
}
}
And WebSecurityConfig as
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsServiceImpl userDetailsService;
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/registration", "/newreport", "/login*", "/signin/**", "/signup/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.csrf().disable()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login").permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
#Override
protected UserDetailsService userDetailsService() {
return userDetailsService;
}
}
Authentication works find if i return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities); inside UserDetailsServiceImpl.
But as soon i return User class object user. Authentication fails.
I am not sure what i am missing here. Any help would be appreciated. Thanks in advance.
Because your UserDetails implementation always return false for the following methods:
#Override
public boolean isAccountNonExpired() {
return false;
}
#Override
public boolean isAccountNonLocked() {
return false;
}
#Override
public boolean isCredentialsNonExpired() {
return false;
}
#Override
public boolean isEnabled() {
return false;
}
In order to pass the authentication , all the above methods should return true.

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

Does the method getUsername() of interface UserDetails indicate that there must be a attribute "username" in an entity impl the interface?

I'm studying springboot on a website. The website gives a example project for spring security, following is a part of the code:
User entity:
#Entity
public class User implements UserDetails {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotEmpty(message = "name no empty")
#Size(min=2, max=20)
#Column(nullable = false, length = 20)
private String name;
#NotEmpty(message = "email no empty")
#Size(max=50)
#Email(message= "wrong email format" )
#Column(nullable = false, length = 50, unique = true)
private String email;
#NotEmpty(message = "username no empty")
#Size(min=3, max=20)
#Column(nullable = false, length = 20, unique = true)
private String username;
#NotEmpty(message = "password no empty")
#Size(max=100)
#Column(length = 100)
private String password;
#Column(length = 200)
private String avatar;
#ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.EAGER)
#JoinTable(name = "user_authority", joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "authority_id", referencedColumnName = "id"))
private List<Authority> authorities;
protected User(){
}
public User(Long id, String name, String username, String email){
this.id = id;
this.name = name;
this.email = email;
this.username = username;
}
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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return username;
}
public void setUsername(String username){
this.username = username;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> simpleAuthorities = new ArrayList<>();
for (GrantedAuthority authority : this.authorities){
simpleAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority()));
}
return simpleAuthorities;
}
public void setAuthorities(List<Authority> authorities) {
this.authorities = authorities;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
public void setPassword(String password) {
this.password = password;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
#Override
public String toString(){
return String.format("User[id = %d, name = '%s', username = '%s', email = '%s']", id, name, username, email);
}
}
UserService implementation:
#Service
public class UserServiceImpl implements UserService, UserDetailsService {
#Autowired
private UserRepository userRepository;
#Transactional
#Override
public User saveOrUpdateUser(User user) {
return userRepository.save(user);
}
#Transactional
#Override
public User registerUser(User user) {
return userRepository.save(user);
}
#Transactional
#Override
public void removeUser(Long id) {
userRepository.delete(id);
}
#Override
public User getUserById(Long id) {
return userRepository.findOne(id);
}
#Override
public Page<User> listUsersByNameLike(String name, Pageable pageable) {
name = "%" + name + "%";//匹配相似
Page<User> users = userRepository.findByNameLike(name, pageable);
return users;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByUsername(username);
}
}
Security Configuration:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
private static final String KEY = "waylau.com";
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder);
return authenticationProvider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/css/**", "/js/**", "/fonts/**", "/index").permitAll()
.antMatchers("/h2-console/**").permitAll()
.antMatchers("/admins/**").hasRole("ADMIN") /
.and()
.formLogin()
.loginPage("/login").failureUrl("/login-error")
.and().rememberMe().key(KEY)
.and().exceptionHandling().accessDeniedPage("/403");
http.csrf().ignoringAntMatchers("/h2-console/**");
http.headers().frameOptions().sameOrigin();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
}
}
What I focus is the attribute "username" of User.java. There is no problem when running codes above.
But after altered "username" to another name such as "accountname", it throws following exception
UserDetailsService returned null...
I know the problem is related to 'getUsername()'. This getter is also a overide method of UserDetails interface.
Take into consideration, the getter becomes this:
#Override
public String getUsername() {
return getAccountname();
}
But still not working. Does it mean the name of the attribute cannot be altered?

Spring Security basic auth always getting 401

I learning Spring, and I integrated Spring security into my current APIs. To keep things simple, I am starting with Basic Auth.
However, the issue that I am facing is that, if I don't provide the credentials, I get the standard 401 along with a JSON response:
{
"timestamp": "2018-07-07T18:40:00.752+0000",
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/courses"
}
But if I do pass correct credentials, I get 401, but without any response body.
Here's my WebSecurityConfiguration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
DetailsService detailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(detailsService)
.passwordEncoder(User.encoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable();
}
}
Here's my DetailsService:
#Component
public class DetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByEmail(username);
if (user == null) {
throw new UsernameNotFoundException("User with email " + username + " was not found");
}
return new org.springframework.security.core.userdetails.User(
user.getEmail(),
user.getPassword(),
AuthorityUtils.createAuthorityList(user.getRoles())
);
}
}
I should point this out that I am looking up user by email instead of username.
Here's my user entity:
#Entity
#Table(name = "users")
public class User extends BaseEntity {
public static final PasswordEncoder encoder = new BCryptPasswordEncoder();
#Column(name = "first_name")
private String firstName;
#JoinColumn(name = "last_name")
private String lastName;
private String email;
#JsonIgnore
private String password;
#JsonIgnore
private String[] roles;
public User(String email, String firstName, String lastName, String password,
String[] roles) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
setPassword(password);
this.roles = roles;
}
// getters and setters
}
First: check that user.getRoles() is not throwing a LazyInitializationException
Second: if hash has been generated online, BCryptPasswordEncoder might not work

Resources