我正在尝试使用jQuery构建功能,该功能将根据标头部分动态创建哈希锚链接。现在,如果有一个带有此哈希的URL,那么我想在页面加载时滚动到该部分。问题在于页面加载,浏览器将滚动到正确的锚点,然后跳回到顶部。这样就可以了,但不会持久。
我知道这里的问题是页面加载时锚标记不存在,所以我必须等待添加元素后才能执行滚动。
我试图做的是:
replaceState
)中删除replaceState
将URL返回到其原始阶段var HeaderAnchors = function(el){
//setup some variables
this.$el = $(el);
this.$headers = this.$el.find( 'h2, h3' );
this.headerSlugs= [];
this.urlHash = window.location.hash;
this.url = window.location.href;
this.init();
};
HeaderAnchors.prototype.init = function(){
new Promise( (resolve, reject) => {
this.createAnchors();
return resolve();
} )
.then( ()=>{
//check if there is hash and if the hash is in the array of headings
if( this.urlHash && this.headerSlugs.includes( this.urlHash.substring(1) ) ){
//disable the anchor from jumping
history.replaceState("", document.title, window.location.pathname);
//smooth scroll to the stop of the hash, which has the same ID as the hash
Foundation.SmoothScroll.scrollToLoc( this.urlHash, {
threshold: 50,
offset: 100,
animationDuration: 200
}, ()=>{
//add the url back to
window.history.replaceState({ path: this.url }, '', this.url);
});
}
} );
};
HeaderAnchors.prototype.createAnchors = function(){
this.$headers.each( ( index, val )=>{
const heading = $(val);
const headingText = heading
.text()
.toLowerCase()
.trim()
.replace( /[^\w\s]/gi, '' )
.replace(/ +/g,'-');
this.headerSlugs.push( headingText );
let hashIcon = $('<span />')
.addClass( 'fas fa-hashtag' )
.attr({
'aria-hidden' : 'true'
});
let anchor = $('<a />')
.attr({
'href' : `#${headingText}`,
'class' : 'c-anchor-hash__link'
})
.append( hashIcon );
heading
.addClass( 'c-anchor-hash' )
.attr( { 'id' : headingText} )
.prepend( anchor );
} );
};
我希望锚点滚动到动态创建的锚点...并且确实如此,但是随后它会跳起来...
任何帮助都会很棒。谢谢!
答案 0 :(得分:0)
解决问题的方法很简单。你太过分了。 只需这样做:
@RestController
public class UserController {
private final ReactiveUserDetailsServiceImpl userService;
private final TokenProvider jwtTokenUtil;
private final JWTReactiveAuthenticationManager authenticationManager;
...
@PostMapping(value="authorize")
public Mono<JWTToken> authorize(LoginUser loginVM) {
if (loginVM ==null || loginVM.getUsername().isEmpty() || loginVM.getPassword().isEmpty()) {
return Mono.error(new RuntimeException("Bad request"));
}
Authentication authenticationToken =
new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword());
Mono<Authentication> authentication = this.authenticationManager.authenticate(authenticationToken);
authentication.doOnError(throwable -> {
throw new BadCredentialsException("Bad crendentials");
});
ReactiveSecurityContextHolder.withAuthentication(authenticationToken);
return authentication.map(auth -> {
String jwt = jwtTokenUtil.createToken(auth);
return new JWTToken(jwt);
});
}
}
@Service
public class ReactiveUserDetailsServiceImpl implements ReactiveUserDetailsService, UserService {
@Autowired
private UserRepository userRepository;
@Override
public Mono<UserDetails> findByUsername(String username) {
return userRepository.findByUsername(username)
.filter(Objects::nonNull)
.switchIfEmpty(Mono.error(new BadCredentialsException(String.format("User %s not found in database", username))))
.map(this::createSpringSecurityUser);
}
private org.springframework.security.core.userdetails.User createSpringSecurityUser(User user) {
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority(user.getRole());
grantedAuthorities.add(grantedAuthority);
return new org.springframework.security.core.userdetails.User(user.getUsername(),
user.getPassword(), grantedAuthorities);
}
public Flux<User> findAll(){
return userRepository.findAll();
}
}
@Component
public class TokenProvider {
private final long validityInMilliseconds = 3600000; // 1h
private static final String SALT_KEY = "123";
private String secretKey;
private final Base64.Encoder encoder = Base64.getEncoder();
private static final String AUTHORITIES_KEY = "auth";
private static final Logger logger = LogManager.getLogger(TokenProvider.class);
@PostConstruct
public void init() {
this.secretKey = encoder.encodeToString(SALT_KEY.getBytes(StandardCharsets.UTF_8));
}
public String createToken(Authentication authentication) {
String authorities = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
long now = (new Date()).getTime();
Date validity = new Date(now + this.validityInMilliseconds);
return Jwts.builder()
.setSubject(authentication.getName())
.claim(AUTHORITIES_KEY, authorities)
.signWith(SignatureAlgorithm.HS512, secretKey)
.setExpiration(validity)
.compact();
}
public Authentication getAuthentication(String token) {
if (StringUtils.isEmpty(token) || !validateToken(token)) {
throw new BadCredentialsException("Invalid token");
}
Claims claims = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody();
Collection<? extends GrantedAuthority> authorities
= Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
User principal = new User(claims.getSubject(), "", authorities);
return new UsernamePasswordAuthenticationToken(principal, token, authorities);
}
private boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(secretKey).parseClaimsJws(authToken);
return true;
} catch (SignatureException e) {
logger.info("Invalid JWT signature.");
logger.trace("Invalid JWT signature trace: {}", e);
} catch (MalformedJwtException e) {
logger.info("Invalid JWT token.");
logger.trace("Invalid JWT token trace: {}", e);
} catch (ExpiredJwtException e) {
logger.info("Expired JWT token.");
logger.trace("Expired JWT token trace: {}", e);
} catch (UnsupportedJwtException e) {
logger.info("Unsupported JWT token.");
logger.trace("Unsupported JWT token trace: {}", e);
} catch (IllegalArgumentException e) {
logger.info("JWT token compact of handler are invalid.");
logger.trace("JWT token compact of handler are invalid trace: {}", e);
}
return false;
}
}
public class JWTReactiveAuthenticationManager implements ReactiveAuthenticationManager {
private final ReactiveUserDetailsServiceImpl userService;
private final PasswordEncoder passwordEncoder;
private static final Logger logger = LogManager.getLogger(JWTReactiveAuthenticationManager.class);
public JWTReactiveAuthenticationManager(final PasswordEncoder passwordEncoder, final ReactiveUserDetailsServiceImpl userService) {
this.passwordEncoder = passwordEncoder;
this.userService = userService;
}
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
if (authentication.isAuthenticated()) {
return Mono.just(authentication);
}
return Mono.just(authentication)
.switchIfEmpty(Mono.defer(this::raiseBadCredentials))
.cast(UsernamePasswordAuthenticationToken.class)
.flatMap(this::authenticateToken)
.publishOn(Schedulers.parallel())
.onErrorResume(e -> raiseBadCredentials())
.filter(u -> passwordEncoder.matches((String) authentication.getCredentials(), u.getPassword()))
.switchIfEmpty(Mono.defer(this::raiseBadCredentials))
.map(u -> new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), u.getAuthorities()));
}
private <T> Mono<T> raiseBadCredentials() {
return Mono.error(new BadCredentialsException("Invalid Credentials"));
}
private Mono<UserDetails> authenticateToken(final UsernamePasswordAuthenticationToken authenticationToken) {
String username = authenticationToken.getName();
logger.info("checking authentication for user " + username);
//todo change, it's not reactive
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
logger.info("authenticated user " + username + ", setting security context");
return userService.findByUsername(username);
}
return null;
}
}
@Configuration
@EnableWebFlux
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SpringSecurityWebFluxConfig {
private ReactiveUserDetailsServiceImpl userService;
private TokenProvider tokenUtil;
private static final String[] AUTH_WHITELIST = {
"/resources/**",
"/webjars/**",
"/authorize/**",
"/favicon.ico",};
public SpringSecurityWebFluxConfig(TokenProvider tokenUtil, ReactiveUserDetailsServiceImpl userService) {
this.tokenUtil = tokenUtil;
this.userService = userService;
}
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, UnauthorizedAuthenticationEntryPoint entryPoint) {
http.httpBasic().disable()
.formLogin().disable()
.csrf().disable()
.logout().disable();
http
.exceptionHandling()
.authenticationEntryPoint(entryPoint)
.and()
.authorizeExchange()
.pathMatchers(HttpMethod.OPTIONS)
.permitAll()
.pathMatchers("/users")
.permitAll()
.and()
.addFilterAt(webFilter(), SecurityWebFiltersOrder.AUTHORIZATION)
.authorizeExchange()
.pathMatchers(AUTH_WHITELIST).permitAll()
.anyExchange().authenticated();
return http.build();
}
@Bean
public AuthenticationWebFilter webFilter() {
AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(repositoryReactiveAuthenticationManager());
authenticationWebFilter.setAuthenticationConverter(new TokenAuthenticationConverter(tokenUtil));
authenticationWebFilter.setRequiresAuthenticationMatcher(new JWTHeadersExchangeMatcher());
authenticationWebFilter.setSecurityContextRepository(new WebSessionServerSecurityContextRepository());
return authenticationWebFilter;
}
@Bean
public JWTReactiveAuthenticationManager repositoryReactiveAuthenticationManager() {
JWTReactiveAuthenticationManager repositoryReactiveAuthenticationManager = new JWTReactiveAuthenticationManager(passwordEncoder(), userService);
return repositoryReactiveAuthenticationManager;
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
$(document).ready(function() {
$("#gen").click(function() {
$("#HTMLdy").html('<a href="https://ess.com.ng">Click Me</a>');
});
});