If you're working on a Spring Security (and especially an OAuth) implementation, definitely have a look at the Learn Spring Security course:
>> LEARN SPRING SECURITYMocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.
Get started with mocking and improve your application tests using our Mockito guide:
Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.
Get started with understanding multi-threaded applications with our Java Concurrency guide:
Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:
Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.
But these can also be overused and fall into some common pitfalls.
To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:
Get started with Spring and Spring Boot, through the Learn Spring course:
>> LEARN SPRINGExplore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:
Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.
I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.
You can explore the course here:
Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.
Get started with Spring Data JPA through the guided reference course:
Refactor Java code safely β and automatically β with OpenRewrite.
Refactoring big codebases by hand is slow, risky, and easy to put off. Thatβs where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.
Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions β one for newcomers and one for experienced users. Youβll see how recipes work, how to apply them across projects, and how to modernize code with confidence.
Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.
β’ The Registration Process With Spring Security
β’ Registration β Activate a New Account by Email
β’ Spring Security Registration β Resend Verification Email
β’ Registration with Spring Security β Password Encoding
β’ The Registration API becomes RESTful
β’ Spring Security β Reset Your Password
β’ Registration β Password Strength and Rules
1. Overview
In this quick article weβll implement a simple βChange my own passwordβ functionality available to the user after they register and log in.
2. Client Side β Change My Password Page
Letβs take a look at the very simple client side page:
<html>
<body>
<div id="errormsg" style="display:none"></div>
<div>
<input id="oldpass" name="oldpassword" type="password" />
<input id="pass" name="password" type="password" />
<input id="passConfirm" type="password" />
<span id="error" style="display:none">Password mismatch</span>
<button type="submit" onclick="savePass()">Change Password</button>
</div>
<script src="jquery.min.js"></script>
<script type="text/javascript">
var serverContext = [[@{/}]];
function savePass(){
var pass = $("#pass").val();
var valid = pass == $("#passConfirm").val();
if(!valid) {
$("#error").show();
return;
}
$.post(serverContext + "user/updatePassword",
{password: pass, oldpassword: $("#oldpass").val()} ,function(data){
window.location.href = serverContext +"/home.html?message="+data.message;
})
.fail(function(data) {
$("#errormsg").show().html(data.responseJSON.message);
});
}
</script>
</body>
</html>
3. Update User Password
Letβs now implement the server side operation as well:
@PostMapping("/user/updatePassword")
@PreAuthorize("hasRole('READ_PRIVILEGE')")
public GenericResponse changeUserPassword(Locale locale,
@RequestParam("password") String password,
@RequestParam("oldpassword") String oldPassword) {
User user = userService.findUserByEmail(
SecurityContextHolder.getContext().getAuthentication().getName());
if (!userService.checkIfValidOldPassword(user, oldPassword)) {
throw new InvalidOldPasswordException();
}
userService.changeUserPassword(user, password);
return new GenericResponse(messages.getMessage("message.updatePasswordSuc", null, locale));
}
Notice how the method is secured via the @PreAuthorize annotation, since it should only accessible to logged in users.
4. API Tests
Finally, letβs consume the API with some API tests to make sure everything is working fine; weβll start with the simple configuration of the test and the data initialization:
@ExtendWith(SpringExtension.class)
@ContextConfiguration(
classes = { ConfigTest.class, PersistenceJPAConfig.class },
loader = AnnotationConfigContextLoader.class)
public class ChangePasswordApiTest {
private final String URL_PREFIX = "http://localhost:8080/";
private final String URL = URL_PREFIX + "/user/updatePassword";
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
FormAuthConfig formConfig = new FormAuthConfig(
URL_PREFIX + "/login", "username", "password");
@BeforeEach
public void init() {
User user = userRepository.findByEmail("[email protected]");
if (user == null) {
user = new User();
user.setFirstName("Test");
user.setLastName("Test");
user.setPassword(passwordEncoder.encode("test"));
user.setEmail("[email protected]");
user.setEnabled(true);
userRepository.save(user);
} else {
user.setPassword(passwordEncoder.encode("test"));
userRepository.save(user);
}
}
}
Now β letβs try to change password for a logged in user:
@Test
public void givenLoggedInUser_whenChangingPassword_thenCorrect() {
RequestSpecification request = RestAssured.given().auth()
.form("[email protected]", "test", formConfig);
Map<String, String> params = new HashMap<String, String>();
params.put("oldpassword", "test");
params.put("password", "newtest");
Response response = request.with().params(params).post(URL);
assertEquals(200, response.statusCode());
assertTrue(response.body().asString().contains("Password updated successfully"));
}
Next β letβs try to change password given a wrong old password:
@Test
public void givenWrongOldPassword_whenChangingPassword_thenBadRequest() {
RequestSpecification request = RestAssured.given().auth()
.form("[email protected]", "test", formConfig);
Map<String, String> params = new HashMap<String, String>();
params.put("oldpassword", "abc");
params.put("password", "newtest");
Response response = request.with().params(params).post(URL);
assertEquals(400, response.statusCode());
assertTrue(response.body().asString().contains("Invalid Old Password"));
}
Finally β letβs try to change password without authentication:
@Test
public void givenNotAuthenticatedUser_whenChangingPassword_thenRedirect() {
Map<String, String> params = new HashMap<String, String>();
params.put("oldpassword", "abc");
params.put("password", "xyz");
Response response = RestAssured.with().params(params).post(URL);
assertEquals(302, response.statusCode());
assertFalse(response.body().asString().contains("Password updated successfully"));
}
Note how β for each test β weβre providing a FormAuthConfig to handle the authentication.
Weβre also resetting the password via init() to make sure we use the correct password before test.
5. Conclusion
And thatβs a wrap β a straightforward way to allow the user to change their own password after registering and logging into the application.
