Reputation: 42957
I am not so into Spring Security. I am working on a Spring Boot project (implementing some REST web sercices) where someone else have implemented Spring Security to perform authentication. It seems works fine but I have some doubts about how it exactly works (the architecture).
So basically I have the following classes:
1) User that is my model class representing a user:
@Entity
@Table(name = "user",
uniqueConstraints = {
@UniqueConstraint(columnNames = {"email","username"})
})
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//@Pattern(regexp="\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b",flags = Pattern.Flag.CASE_INSENSITIVE)
@Column(name="email", unique=true,nullable=false)
private String email;
@NotNull
@Column(name="registration_date",nullable=false)
private Date registration_date;
@NotBlank
@Column(name="username",nullable = false,unique=true)
private String username;
@NotBlank
@Column(name="password",nullable = false)
private String password;
@Column(name="enabled", nullable = false)
private boolean enabled;
@ManyToMany
@JoinTable(name = "user_user_roles", joinColumns = {
@JoinColumn(name = "id_user", updatable = true) },
inverseJoinColumns = { @JoinColumn(name = "id_roles",
updatable = true)},
uniqueConstraints={@UniqueConstraint(columnNames = {"id_user","id_roles"})}
)
@Cascade({CascadeType.DETACH,CascadeType.MERGE,CascadeType.REFRESH,CascadeType.PERSIST,
CascadeType.SAVE_UPDATE})
private List<UserRole> userRoles;
// CONSTRUCTOR, GETTER AND SETTER METHODS
}
It is an hibernate class that provide the join with the user_user_roles database table containing the list of user rool associated to the specific user (I think the ROLE_ADMIN, ROLE_USER, etcetc)
2) Then I have CustomUserDetails class that extends User and implements Spring Security UserDetails interface.
So it means that will contain all the information related to a specific user (among which the role associated to this user) and implement all the methods declared in the UserDetails interface.
public class CustomUserDetails extends User implements UserDetails {
private static final long serialVersionUID = 1L;
public CustomUserDetails(User user){
super(user);
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
for(UserRole role : this.getUserRoles() ){
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getName());
authorities.add(grantedAuthority);
}
return 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;
}
@Override
public String getUsername() {
return super.getUsername();
}
}
It seems to me that this class represent the bridge between Spring application and Spring Security (something like that carry the roles information related to a specific user, is it?).
In this class the information related to the list of roles of an user is contained into a collection of generic object that extends GrantedAuthority.
I think that the GrantedAuthority objects represents the roles of a user (infact are builded by role.getName() that is the string representing a role of the current user). Is it this reasoning correct?
Basically the prvious implementation only return the collection of GrantedAuthority related to a specific user, is it?
3) The CustomUserDetailsService class implementing Spring Security UserDetailsService interface:
@Transactional
@Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{
@Autowired
private UserDAO userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.findByUsername(username);
if(user == null){
throw new UsernameNotFoundException("No user present with username: "+username);
}else{
return new CustomUserDetails(user);
}
}
}
Basically this is a service that only implements the loadUserByUsername(String username)) method that do:
First retrieve the information related the user (a User object) including the list of his roles (List userRoles).
Then use this User object to build the previous CustomUserDetails used to retrieve the collection of the GrantedAuthority related to the user.
Is all these reasoning correct?
Then I also have this WebSecurityConfig that I think represent the Spring Security configuration:
@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = CustomUserDetailsService.class)
@EnableAutoConfiguration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
}
}
This is pretty obsucre for me. What exactly does?
From what I have understand it is enabling the REST web services security by ** @EnableWebSecurity**.
But why does the autowire of the UserDetailsService bean?
And what exactly foes this empty configure() method?
Upvotes: 1
Views: 4530
Reputation: 2470
Your reasoning is correct.
Spring-security requires you to create a service which implements UserDetailsService. It expects service to have loadUserByUsername method which returns user object (which needs to implement Spring's User class). This instance of user is used to get authorities so that you can restrict access to certain urls. i.e. you can map url access to users with specific authorities.
In terms of the empty method configure(), it is used to for authentication and authorisation of the urls (mentioned above) and few more things. In fact, in terms of security, this is most flexible & powerful method available.
My project's config method looks like this:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/","/static/**").permitAll()
.mvcMatchers("/admin").access("hasAuthority('ROLE_ADMIN')")
.mvcMatchers("/employees").access("hasAuthority('ROLE_STAFF')")
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.logout().logoutSuccessUrl("/")
.and()
.headers().contentSecurityPolicy("default-src 'self' " +
"https://ajax.googleapis.com " +
"https://cdnjs.cloudfare.com " +
"style-src 'self' 'unsafe-inline' ");
}
The above example
To understand more about all spring-security features, I highly recommend these resources.
Upvotes: 5