Write and Publish a Tutorial!
Do you have good notes or papers written by you and seeking for a
platform to publish? We provide the platform to publish your tutorials
in your name. If you wish to publish your tutorial in your name to
help the readers, Please contact us by sending an email to
publish@tools4testing.com or publish@java4coding.com The main way that
others learn about your work is through your published tutorials. If
you don’t publish, it will be as if you never did the work. Your notes
can help the readers only when you share it.
Adding Updating and Deleting Users in Spring Security
If you need custom user management operations, implement the UserDetailsManager interface. This can involve creating, updating, deleting, and retrieving user details according to your application's requirements. If the app only needs to authenticate the users, then implementing the UserDetailsService contract is enough to cover the desired functionality. When implementing the UserDetailsManager interface you have to implement five methods of its own and one which it inherits from the UserDetailsService which it extends. These methods are: createUser, updateUser, deleteUser, changePassword, userExists and the inherited method loadUserByUsername.
public interface UserDetailsManager extends UserDetailsService {
void createUser(UserDetails user);
void updateUser(UserDetails user);
void deleteUser(String username);
void changePassword(String oldPassword, String newPassword);
boolean userExists(String username);
}
In the Spring Security framework v2.0.4 there are two concrete implementations of UserDetailsManager: JdbcUserDetailsManager and LdapUserDetailsManager. Generally, we don’t implement this interface or even we don’t use JdbcUserDetailsManager and LdapUserDetailsManager. After authentication/login use project specific flows to create, update, delete user. This interface is included purely as a convenience interface which the user of the framework may or may not decide to use.
Example
pom.xml<?xml version="1.0" encoding="UTF-8"?> <project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.0</version> <relativePath/> <!-- lookup parent from repository, not local --> </parent> <groupId>com.java4coding</groupId> <artifactId>AddingModifyingDeletingUser</artifactId> <version>0.0.1-SNAPSHOT</version> <name>AddingModifyingDeletingUser</name> <description>Managing Users in Spring Security</description> <properties> <java.version>11</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-boot.version>2.6.0</spring-boot.version> <lombok.version>1.18.28</lombok.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</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-security</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <executions> <execution> <id>build-info</id> <goals> <goal>build-info</goal> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
</project> |
SpringBootDemo.javapackage com.java4coding;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@SpringBootApplication @EntityScan @EnableJpaRepositories public class SpringBootDemo { public static void main(String[] args) { SpringApplication.run(SpringBootDemo.class, args); } } |
ApplicationConfig.javapackage com.java4coding.config;
import com.java4coding.repository.UserRepository; import com.java4coding.security.UserDetailsServiceImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration public class ApplicationConfig {
@Bean public UserDetailsService userDetailsService(UserRepository userRepository) { return new UserDetailsServiceImpl(userRepository); }
@Bean public PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); } } |
ApplicationWebSecurityConfigurerAdapter.javapackage com.java4coding.config;
import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration public class ApplicationWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
/* Since H2 has its own authentication provider, you can skip the Spring Security for the path of h2 console entirely in the same way that you do for your static content. In order to do that, in your Spring security config, you have to override the configuration method which takes an instance of org.springframework.security.config.annotation.web.builders.WebSecurity as a parameter instead of the one which takes an instance of org.springframework.security.config.annotation.web.builders.HttpSecurity */ @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/h2-console/**"); }
/* Spring Security enables Cross-Site Request Forgery (CSRF) protection by default. CSRF is an attack that tricks the victim into submitting a malicious request and uses the identity of the victim to perform an undesired function on their behalf. If the CSRF token, which is used to protect against this type of attack, is missing or incorrect, the server may also respond with error 403. */ @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest() .authenticated() .and() .httpBasic(); http.csrf().disable(); }
} |
UserController.javapackage com.java4coding.controller;
import com.java4coding.dto.ResponseDto; import com.java4coding.dto.UserDto; import com.java4coding.entity.User; import com.java4coding.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController public class UserController {
@Autowired private UserRepository userRepository;
@GetMapping(value = "/testAccess") public String testAccess() { return "Hurray! You are Authorized."; }
@PostMapping(value = "/addUser") public ResponseEntity<ResponseDto> addUser(@RequestBody UserDto userDto) { User user = new User(); user.setPassword(userDto.getPassword()); user.setUsername(userDto.getUsername()); user.setAuthority(userDto.getAuthority()); userRepository.save(user); return ResponseEntity.ok(ResponseDto.builder().result("Success").code("200").build()); }
@PostMapping(value = "/updateUser") public ResponseEntity<ResponseDto> updateUser(@RequestBody UserDto userDto) { List<User> users = userRepository.findByUsername(userDto.getUsername()); if (users == null || users.isEmpty()) { return ResponseEntity.ok(ResponseDto.builder().result("No User Exists").code("400").build()); } User existingUser = users.get(0); existingUser.setPassword(userDto.getPassword()); existingUser.setUsername(userDto.getNewUsername()); existingUser.setAuthority(userDto.getAuthority()); userRepository.save(existingUser); return ResponseEntity.ok(ResponseDto.builder().result("Success").code("200").build()); }
@GetMapping(value = "/deleteUser/{userName}") public ResponseEntity<ResponseDto> deleteUser(@PathVariable("userName") String userName) { List<User> users = userRepository.findByUsername(userName); if (users == null || users.isEmpty()) { return ResponseEntity.ok(ResponseDto.builder().result("No User Exists").code("400").build()); } User existingUser = users.get(0); userRepository.delete(existingUser); return ResponseEntity.ok(ResponseDto.builder().result("Success").code("200").build()); } } |
ResponseDto.javapackage com.java4coding.dto;
import lombok.Builder; import lombok.Getter; import lombok.Setter;
@Setter @Getter @Builder public class ResponseDto { private String result; private String code; } |
UserDto.javapackage com.java4coding.dto;
import lombok.Getter; import lombok.Setter;
@Setter @Getter public class UserDto { private String username; private String newUsername; private String password; private String authority; } |
User.javapackage com.java4coding.entity;
import lombok.Getter; import lombok.Setter;
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id;
@Entity @Setter @Getter public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; private String authority; } |
UserRepository.javapackage com.java4coding.repository;
import com.java4coding.entity.User; import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface UserRepository extends JpaRepository<User, Long> { List<User> findByUsername(String userName); } |
SecurityUser.java package com.java4coding.security;
import com.java4coding.entity.User; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection; import java.util.List;
public class SecurityUser implements UserDetails { private final User user;
public SecurityUser(User user) { this.user = user; }
@Override public String getUsername() { return user.getUsername(); }
@Override public String getPassword() { return user.getPassword(); }
@Override public Collection<? extends GrantedAuthority> getAuthorities() { return List.of(() -> user.getAuthority()); }
@Override public boolean isAccountNonExpired() { return true; }
@Override public boolean isAccountNonLocked() { return true; }
@Override public boolean isCredentialsNonExpired() { return true; }
@Override public boolean isEnabled() { return true; } } |
UserDetailsServiceImpl.javapackage com.java4coding.security;
import com.java4coding.entity.User; import com.java4coding.repository.UserRepository; import lombok.AllArgsConstructor; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.List;
@AllArgsConstructor public class UserDetailsServiceImpl implements UserDetailsService { private UserRepository userRepository;
@Override public SecurityUser loadUserByUsername(String username) throws UsernameNotFoundException { List<User> usersByUserName = userRepository.findByUsername(username); return new SecurityUser(usersByUserName.get(0)); } } |
application.propertiesspring.h2.console.enabled=true spring.h2.console.path=/h2-console/
spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password=pass spring.jpa.database-platform=org.hibernate.dialect.H2Dialect #spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.show_sql=true spring.jpa.properties.hibernate.use_sql_comments=true spring.jpa.properties.hibernate.format_sql=true |
schema.sqlcreate table user ( id bigint generated by default as identity, authority varchar(255), password varchar(255), username varchar(255), primary key (id) ); |
data.sqlINSERT INTO User (id, username, password, authority) VALUES (1, 'manu', 'pass', 'read'); INSERT INTO User (id, username, password, authority) VALUES (2, 'advith', 'xyz123', 'read'); INSERT INTO User (id, username, password, authority) VALUES (3, 'aashvith', 'xyz123', 'read'); |
Project Structure
Output