如何在Java Filter中重定向遗留用户至AngularJS密码重置页?
Got it, let's break this down into backend (Spring Security) and frontend (AngularJS) steps—this flow needs coordination between both layers to make it seamless for legacy users.
Backend: Spring Security Configuration & Custom Logic
First, we need to detect if a user is a legacy user requiring password reset, then redirect them appropriately after login.
1. Add a Flag to User Entity
Add a boolean field (like needsPasswordReset) to your user entity to track if the user needs to reset their password. For legacy users, set this to true initially; new users get false.
@Entity public class AppUser { // Existing fields (username, password, roles, etc.) private boolean needsPasswordReset; // Getters and setters public boolean isNeedsPasswordReset() { return needsPasswordReset; } public void setNeedsPasswordReset(boolean needsPasswordReset) { this.needsPasswordReset = needsPasswordReset; } }
2. Custom AuthenticationSuccessHandler
Create a custom handler to check the needsPasswordReset flag after successful authentication. If it's true, redirect to your AngularJS password reset route; else, send the user to the normal dashboard.
@Component public class CustomAuthSuccessHandler implements AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { AppUser user = (AppUser) authentication.getPrincipal(); if (user.isNeedsPasswordReset()) { // Redirect to Angular's password reset route (adjust path to match your frontend routing) response.sendRedirect("/#/reset-password"); } else { // Redirect to your default authenticated landing page response.sendRedirect("/#/dashboard"); } } }
3. Update Spring Security Config
Wire up your custom success handler in your SecurityFilterChain configuration, and ensure the reset password route is accessible:
@Configuration @EnableWebSecurity public class SecurityConfig { private final CustomAuthSuccessHandler authSuccessHandler; private final PasswordEncoder passwordEncoder; public SecurityConfig(CustomAuthSuccessHandler authSuccessHandler, PasswordEncoder passwordEncoder) { this.authSuccessHandler = authSuccessHandler; this.passwordEncoder = passwordEncoder; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/login", "/api/auth/reset-password", "/#/reset-password").permitAll() .anyRequest().authenticated() ) .formLogin(form -> form .successHandler(authSuccessHandler) // Use our custom success handler .permitAll() ) .logout(logout -> logout.permitAll()); return http.build(); } }
4. Password Reset Endpoint
Create an API endpoint to handle password updates and toggle the needsPasswordReset flag once the reset is complete:
@RestController @RequestMapping("/api/auth") public class AuthController { private final UserDetailsManager userDetailsManager; private final PasswordEncoder passwordEncoder; public AuthController(UserDetailsManager userDetailsManager, PasswordEncoder passwordEncoder) { this.userDetailsManager = userDetailsManager; this.passwordEncoder = passwordEncoder; } @PostMapping("/reset-password") public ResponseEntity<?> resetPassword(@RequestBody PasswordResetRequest request) { try { AppUser user = (AppUser) userDetailsManager.loadUserByUsername(request.getUsername()); // Encode new password and update user user.setPassword(passwordEncoder.encode(request.getNewPassword())); user.setNeedsPasswordReset(false); userDetailsManager.updateUser(user); return ResponseEntity.ok("Password reset successfully"); } catch (UsernameNotFoundException e) { return ResponseEntity.badRequest().body("User not found"); } } // DTO for password reset request public static class PasswordResetRequest { private String username; private String newPassword; // Getters and setters public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getNewPassword() { return newPassword; } public void setNewPassword(String newPassword) { this.newPassword = newPassword; } } }
Frontend: AngularJS Routing & Reset Flow
Now, set up the frontend to handle the redirect and password reset form, with validation for your new password rules.
1. Configure Routes
Add a route for the password reset page in your AngularJS app config, and add a guard to enforce redirects if a user hasn't reset their password yet:
angular.module('myApp', ['ngRoute']) .config(['$routeProvider', function($routeProvider) { $routeProvider .when('/dashboard', { templateUrl: 'dashboard.html', controller: 'DashboardController', resolve: { authCheck: function(AuthService, $location) { return AuthService.getCurrentUser().then(user => { if (user.needsPasswordReset) { $location.path('/reset-password'); return false; } return true; }); } } }) .when('/reset-password', { templateUrl: 'reset-password.html', controller: 'ResetPasswordController', resolve: { auth: function(AuthService) { return AuthService.isAuthenticated(); } } }) .otherwise({redirectTo: '/login'}); }]);
2. Password Reset Controller
Create a controller to handle form submission and communicate with the backend:
angular.module('myApp') .controller('ResetPasswordController', ['$scope', '$http', '$location', 'AuthService', function($scope, $http, $location, AuthService) { $scope.currentUser = AuthService.getCurrentUser(); $scope.errorMessage = ''; $scope.resetPassword = function() { if (!$scope.newPassword || !$scope.newPassword.match(/^(?=.*[A-Z])(?=.*[0-9])(?=.{8,})/)) { $scope.errorMessage = 'Password must be at least 8 characters, include one uppercase letter and one number.'; return; } const resetRequest = { username: $scope.currentUser.username, newPassword: $scope.newPassword }; $http.post('/api/auth/reset-password', resetRequest) .then(function(response) { Alert('Password reset successful! Redirecting to dashboard...'); // Update local user data $scope.currentUser.needsPasswordReset = false; AuthService.setCurrentUser($scope.currentUser); $location.path('/dashboard'); }) .catch(function(error) { $scope.errorMessage = 'Failed to reset password. Please try again.'; }); }; }]);
3. Reset Password Template (reset-password.html)
Create a user-friendly form with validation hints:
<div class="reset-container"> <h2>Update Your Password</h2> <p>Your password must meet our new security requirements:</p> <ul> <li>At least 8 characters long</li> <li>Contains one uppercase letter</li> <li>Contains one number</li> </ul> <div ng-if="errorMessage" class="error">{{errorMessage}}</div> <form ng-submit="resetPassword()"> <div class="form-group"> <label>New Password:</label> <input type="password" ng-model="newPassword" required> </div> <button type="submit" ng-disabled="!newPassword">Save New Password</button> </form> </div>
Key Edge Cases to Handle
- Route Guards: Ensure legacy users can't access other routes until they reset their password (the resolve block in the dashboard route helps with this).
- Session Security: After password reset, consider invalidating the old session and creating a new one (you can add this logic in the backend reset endpoint).
- Client-Side Validation: Match your frontend password rules exactly to the backend to avoid unnecessary API calls.
内容的提问来源于stack exchange,提问作者uncommon_breed

