所以,我尝试了教程中的 Vaadin 登录组件 https://vaadin.com/docs/latest/security/enabling-security,它的工作原理就像一个魅力。
现在我正在尝试自定义登录表单,但每当我登录时,当我尝试导航到任何其他页面时,我都会被注销。请帮忙。
登录表格:
@Route("login")
@PageTitle("Login")
@AnonymousAllowed
public class LoginViewOverLay extends Div implements BeforeEnterObserver, ComponentEventListener<AbstractLogin.LoginEvent> {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
SecurityService securityservice;
@Autowired UserDetailsServiceImpl userdetails;
FormLayout form = new FormLayout();
LoginOverlay loginOverlay = new LoginOverlay();
public LoginViewOverLay() {
add(loginOverlay);
loginOverlay.setOpened(true);
loginOverlay.addLoginListener(this);
}
@Override
public void onComponentEvent(AbstractLogin.LoginEvent loginEvent) {
try {
securityservice.authenticateUser(loginEvent.getUsername(), loginEvent.getPassword());
loginOverlay.close();
getUI().ifPresent(ui -> ui.navigate("/"));
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
if(beforeEnterEvent.getLocation()
.getQueryParameters()
.getParameters()
.containsKey("error")) {
loginOverlay.setError(true);
}
}
}
我的保安服务类别:
@Component
public class SecurityService {
@Autowired
UserDetailsServiceImpl userdetails;
@Autowired
PasswordEncoder encoder;
private final AuthenticationContext authenticationContext;
public SecurityService(AuthenticationContext authenticationContext) {
this.authenticationContext = authenticationContext;
}
public UserDetails getAuthenticatedUser() {
return authenticationContext.getAuthenticatedUser(UserDetails.class).get();
}
public void logout() {
authenticationContext.logout();
}
public UserDetails getAuthenticatedUser2() {
SecurityContext context = SecurityContextHolder.getContext();
Object principal = context.getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
return (UserDetails) context.getAuthentication().getPrincipal();
}
// Anonymous or no authentication.
return null;
}
public void authenticateUser(UserDetails userDetails) {
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
public void authenticateUser(String username, String password) {
UserDetails userDetails = userdetails.loadUserByUsername(username);
if (encoder.matches(password, userDetails.getPassword())) {
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
// System.out.println("wrong");
}
}
}
我的用户详细信息类别:
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
public UserDetailsServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
@Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserLogin user = userRepository.findByUserNameAndEnabled(username, true);
if (user == null) {
throw new UsernameNotFoundException("No user present with username: " + username);
} else {
//System.out.println("Yes User");
return new User(user.getUserName(), user.getHashedPassword(), getAuthorities(user));
}
}
private static List<GrantedAuthority> getAuthorities(UserLogin user) {
return user.getRoles().stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role.getRoleName()))
.collect(Collectors.toList());
}
}
安全配置类:
@EnableWebSecurity
@Configuration
public class SecurityConfiguration extends VaadinWebSecurity {
@Autowired
UserDetailsServiceImpl userdetails;
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorize -> authorize
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.GET, "/images/*.png")).permitAll()
// .requestMatchers(new AntPathRequestMatcher("/**",
// HttpMethod.POST.toString())).permitAll()
.requestMatchers(new AntPathRequestMatcher("/**", HttpMethod.DELETE.toString())).denyAll()
.requestMatchers(new AntPathRequestMatcher("/**", HttpMethod.OPTIONS.toString())).denyAll()
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.OPTIONS, "/**")).denyAll()
).sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED).invalidSessionUrl("/")
.maximumSessions(1).sessionRegistry(sessionRegistry()).expiredUrl("/")
.maxSessionsPreventsLogin(false));
super.configure(http);
setLoginView(http, LoginViewOverLay.class);
}
@Bean
SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
}
使用 Spring Security 6.0+ 时出现此问题
[Spring 安全文档][1] [1]:https://docs.spring.io/spring-security/reference/servlet/authentication/session-management.html
为了防止出现此问题,请使用 SecurityContextRepository 保存 SecurityContext,如下所示:
public void authenticateUser() {
Authentication authentication = new UsernamePasswordAuthenticationToken(loginEvent.getUsername(), loginEvent.getPassword());
Authentication authenticated = authenticationManager.authenticate(authentication);
SecurityContextHolder.getContext().setAuthentication(authenticated);
SecurityContext context = SecurityContextHolder.getContext();
securityRepo.saveContext(context, VaadinServletRequest.getCurrent(), VaadinServletResponse.getCurrent());
if (authenticated.isAuthenticated()) {
UI.getCurrent().navigate(DashboardView.class);
} else {
System.out.println("FAILURE");
Notification.show("Authentication failed", 3000,
Notification.Position.BOTTOM_CENTER); }
}