Dependencies
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
application.properties
server.port = 8888
##################
# Mysql
##################
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
spring.jpa.show-sql= false
Firstly, we need to understand ManyToMany relationship in MySQL how to do CRUD operations.
Since User and Role are ManyToMany relationship
User.java
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "users")
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "user_id")
private Long id;
@Column(name = "username")
private String username;
@Column(name = "email")
private String email;
@Column(name = "password")
private String password;
@Column(name = "name")
private String name;
@Column(name = "lastName")
private String lastName;
@Column(name = "enabled")
private Boolean enabled = true;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "user_role",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();
public User(String username, String password) {
this.username = username;
this.password = password;
}
public void addRole(Role role) {
this.roles.add(role);
role.getUsers().add(this);
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
System.out.println("[Shark]" + roles);
List<SimpleGrantedAuthority> rs = new ArrayList<>();
for (Role r : roles) {
rs.add(new SimpleGrantedAuthority(r.getName()));
}
return rs;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
}
Role.java
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "roles")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "role_id")
private Long id;
@Column(name = "name")
private String name;
@ManyToMany(mappedBy = "roles", fetch = FetchType.LAZY)
private Set<User> users = new HashSet<>();
public Role(String name) {
this.name = name;
}
}
UserRepository.java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmail(String email);
User findByUsername(String username);
}
RoleRepository.java
@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
Role findByName(String name);
}
MyUserDetailsService.java
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("[Shark] " + username);
User u = userRepository.findByUsername(username);
System.out.println("[Shark] " + u.getRoles());
if (u == null ){
throw new UsernameNotFoundException("cannot find the user");
} else {
return u;
}
}
}
DataInitListener.java
@Component
public class DataInitListener implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
UserRepository userRepository;
@Autowired
RoleRepository roleRepository;
private boolean alreadySetup = false;
public Boolean checkSetup() {
if (alreadySetup) return true;
userRepository.deleteAll();
roleRepository.deleteAll();
return false;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (checkSetup()) { return; }
Role r1 = new Role("ROLE_ADMIN");
Role r2 = new Role("ROLE_USER");
User u1 = new User("myadmin", bCryptPasswordEncoder.encode("myadmin"));
User u2 = new User("myuser", bCryptPasswordEncoder.encode("myuser"));
u1.getRoles().add(r1);
u2.getRoles().add(r2);
userRepository.save(u1);
userRepository.save(u2);
alreadySetup = true;
}
}
Secondly, we need to know how to configure Spring Security.
We would have a test resource controller
HomeController.java
WebSecurityConfig.java
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public MyUserDetailsService userDetailsService() {
return new MyUserDetailsService();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.antMatchers("/user").hasAnyRole("ADMIN", "USER")
.antMatchers("/").permitAll()
.and().formLogin();
}
}