我正在编写一个应用程序,前端用户将通过API调用后端。我已经实现了JWT,用户可以在其中注册,当他们尝试登录时,他们将获得JWT作为响应,然后我可以使用标题中的JWT进行其他调用。如果JWT被排除在标题之外,则呼叫将失败。基础工作按预期工作。 我面临的问题是我可以生成自己的自定义JWT,将其分配给标题并能够成功调用后端。 我在线跟踪了很多教程,发现这个主题非常混乱,而且非常复杂,无法完全掌握。我毫不怀疑这是我在代码中遗漏的一些简单但我无法看到的内容。
这是我到目前为止所做的事情;
AuthenticationFilter
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
User credentials = new ObjectMapper().readValue(request.getInputStream(), User.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
credentials.getUsername(),
credentials.getPassword(),
new ArrayList<>()
)
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
String token = Jwts.builder()
.setSubject(((org.springframework.security.core.userdetails.User) authResult.getPrincipal()).getUsername())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET.getBytes())
.compact();
response.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
}
}
AuthorizationFilter
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String header = request.getHeader(HEADER_STRING);
if(StringUtils.isBlank(header) || ! StringUtils.startsWith(header, TOKEN_PREFIX)){
chain.doFilter(request, response);
return;
}
UsernamePasswordAuthenticationToken authenticationToken = getAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if(StringUtils.isNotBlank(token)){
String user = Jwts.parser()
.setSigningKey(SECRET.getBytes())
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody()
.getSubject();
if(StringUtils.isNotBlank(user)){
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
WebSecurity
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
@Bean
CorsConfigurationSource corsConfigurationSource(){
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
正如我所说,我很难找到这个主题,所以我确信我的代码中可能存在简单的错误。如果您能以任何方式帮助或解决我的问题,我们将非常感激
答案 0 :(得分:1)
这似乎是来自servlet过滤器的关键代码,用于检查JWT:
String token = request.getHeader(HEADER_STRING);
if (StringUtils.isNotBlank(token)){
String user = Jwts.parser()
.setSigningKey(SECRET.getBytes())
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody()
.getSubject();
if (StringUtils.isNotBlank(user)){
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
如果你仔细观察你在做什么,你会发现你的逻辑是从JWT中提取主题。这很正确。但只要主题不是空白,您就可以授权该用户。换句话说,任何用户将被您当前的逻辑授权。相反,你通常会做这样的事情:
if (StringUtils.isNotBlank(user)){
// check that user against a database/cache
// if the account is active etc. THEN authorize the user
}
通常,在从JWT中提取主题/用户名后,您将点击数据库/缓存以检查该用户的帐户是否仍处于活动状态。如果没有,那么你将401返回到调用应用程序。 JWT本身并不意味着用户已获得授权,因为在某些时候您可以撤销该用户的令牌,令牌可能会过期等等。