英文:
JWT authentication with spring and angular with null header
问题
您正在尝试使用Spring Boot和Angular进行JWT令牌身份验证。在登录后,会创建Bearer令牌,但在JWTAuthorizationFilter中获取到的标头为null,因此返回anonymousUser。请告诉我为什么会获取到null标头。
以下是您提供的代码的翻译部分:
SecurityConfig.java
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
private CustomUserDetailService customUserDetailService;
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())
.and().csrf().disable()
.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers("/manage/**").hasRole("ADMIN")
.antMatchers("/").hasRole("USER")
.and()
.exceptionHandling()
.accessDeniedPage("/access-denied")
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager(), customUserDetailService));
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailService).passwordEncoder(new BCryptPasswordEncoder());
}
}
JWTAuthenticationFilter.java
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 {
UserDetail user = new ObjectMapper().readValue(request.getInputStream(), UserDetail.class);
return this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
String username = ((org.springframework.security.core.userdetails.User) authResult.getPrincipal()).getUsername();
String token = Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
String bearerToken = TOKEN_PREFIX + token;
System.out.println(bearerToken);
response.getWriter().write(bearerToken);
response.addHeader(HEADER_STRING, bearerToken);
}
}
JWTAuthorizationFilter.java
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
private final CustomUserDetailService customUserDetailService;
public JWTAuthorizationFilter(AuthenticationManager authenticationManager, CustomUserDetailService customUserDetailService) {
super(authenticationManager);
this.customUserDetailService = customUserDetailService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String header = request.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(request, response);
return;
}
UsernamePasswordAuthenticationToken authenticationToken = getAuthenticationToken(request);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthenticationToken(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token == null) return null;
String username = Jwts.parser().setSigningKey(SECRET)
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody()
.getSubject();
UserDetails userDetails = customUserDetailService.loadUserByUsername(username);
return username != null ?
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities())
: null;
}
}
CustomUserDetailService.java
@Component
public class CustomUserDetailService implements UserDetailsService {
private List<GrantedAuthority> role;
@Autowired
private UserDAO userDAO;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = Optional.ofNullable(userDAO.getByEmail(username))
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
List<GrantedAuthority> authorityListAdmin = AuthorityUtils.createAuthorityList("ROLE_ADMIN");
List<GrantedAuthority> authorityListUser = AuthorityUtils.createAuthorityList("ROLE_USER");
if ("admin".equals(user.getRole())) {
role = authorityListAdmin;
} else {
role = authorityListUser;
}
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), role);
}
}
Userinfo.java
private String email;
private String role;
private String password;
Controller
@RequestMapping(value = "/login")
public ModelAndView login(
@RequestParam(name = "error", required = false) String error,
@RequestParam(name = "logout", required = false) String logout,
HttpServletRequest request,
HttpServletResponse response) {
ModelAndView mv = new ModelAndView("login");
HttpSession session = request.getSession(false);
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println("auth === " + auth);
System.out.println("logout === " + logout);
return mv;
}
这是控制台的输出。请告诉我您在这里遗漏了什么。
英文:
I am trying to do JWT token authentication using spring boot and angular. After login bearer token is created but after that in JWTAuthorizationFilter i am getting null header and because of that it return anonymousUser. Please tell me why i am getting null header.
SecurityConfig.java
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
private CustomUserDetailService customUserDetailService;
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.
cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())
.and().csrf().disable()
.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers("/manage/**").hasRole("ADMIN")
.antMatchers("/").hasRole("USER")
.and()
.exceptionHandling()
.accessDeniedPage("/access-denied")
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager(), customUserDetailService));
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailService).passwordEncoder(new
BCryptPasswordEncoder());
}
}
JWTAuthenticationFilter.java
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 {
UserDetail user = new ObjectMapper().readValue(request.getInputStream(), UserDetail.class);
return this.authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
Authentication authResult) throws IOException, ServletException {
String username = ((org.springframework.security.core.userdetails.User) authResult.getPrincipal()).getUsername();
String token = Jwts
.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
String bearerToken = TOKEN_PREFIX + token;
System.out.println(bearerToken);
response.getWriter().write(bearerToken);
response.addHeader(HEADER_STRING, bearerToken);
}
}
JWTAuthorizationFilter.java
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
private final CustomUserDetailService customUserDetailService;
public JWTAuthorizationFilter(AuthenticationManager authenticationManager, CustomUserDetailService customUserDetailService) {
super(authenticationManager);
this.customUserDetailService = customUserDetailService;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
String header = request.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(request, response);
return;
}
UsernamePasswordAuthenticationToken authenticationToken = getAuthenticationToken(request);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthenticationToken(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token == null) return null;
String username = Jwts.parser().setSigningKey(SECRET)
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody()
.getSubject();
UserDetails userDetails = customUserDetailService.loadUserByUsername(username);
return username != null ?
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities())
: null;
}
}
CustomUserDetailService.java
@Component
public class CustomUserDetailService implements UserDetailsService {
private List<GrantedAuthority> role;
@Autowired
private UserDAO userDAO;
/*
* @Autowired public CustomUserDetailService(UserRepository userRepository) {
* this.userRepository = userRepository; }
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = Optional.ofNullable(userDAO.getByEmail(username))
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
List<GrantedAuthority> authorityListAdmin = AuthorityUtils.createAuthorityList("ROLE_ADMIN");
List<GrantedAuthority> authorityListUser = AuthorityUtils.createAuthorityList("ROLE_USER");
if (user.getRole() == "admin") {
role = authorityListAdmin;
} else {
role = authorityListUser;
}
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), role);
}
}
Userinfo.java
private String email;
private String role;
private String password;
Controller
@RequestMapping(value = "/login")
public ModelAndView login(
@RequestParam(name = "error", required = false) String error,
@RequestParam(name = "logout", required = false) String logout,
HttpServletRequest request,
HttpServletResponse response) {
ModelAndView mv = new ModelAndView("login");
HttpSession session= request.getSession(false);
Authentication auth = SecurityContextHolder.getContext()
.getAuthentication();
System.out.println("auth ===" + auth);
System.out.println("logout ===" + logout);
return mv;
}
This is the output on console:
Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJidW50QGdtYWlsLmNvbSIsImV4cCI6MTU5NjExMjcwM30.fBFMDO--8Q_56LT_qbioiT6p3BOxk3L9OrPVTw5EGbf7oJ0ky7W7PuahIYcdjYSL6-OsHY6qq8tPEetlJO7nEg
auth ===org.springframework.security.authentication.AnonymousAuthenticationToken@823df96a: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
Please tell me what i am missing here.
答案1
得分: 1
你的 JWTAuthenticationFilter
继承了 UsernamePasswordAuthenticationFilter
,并覆盖了 successfulAuthentication
方法,默认情况下会调用以下代码:
SecurityContextHolder.getContext().setAuthentication(authResult);
你的实现中没有这行代码,因此在处理完该过滤器后,Spring 上下文中仍然没有 Authentication
对象。接下来调用的是你的 JWTAuthorizationFilter
,它试图从与前一个过滤器中相同的 request
对象中读取标头。JWTAuthenticationFilter
将此标头设置在 response
对象中,而不是 request
对象中。因此,基本上在登录流程后,你无法进行身份验证,因为 if (header == null || !header.startsWith(TOKEN_PREFIX))
始终为真。
英文:
Your JWTAuthenticationFilter
that extends UsernamePasswordAuthenticationFilter
overrides successfulAuthentication
method which by default calls this line:
SecurityContextHolder.getContext().setAuthentication(authResult);
Your implementation do not have this line so after processing of this filter you still do not have Authentication
in Spring context. The next filter that is called is your JWTAuthorizationFilter
which tries to read header from same request
object as in previous filter. JWTAuthenticationFilter
sets this header in response
object not in request
object. So basically you ends up without authentication because if (header == null || !header.startsWith(TOKEN_PREFIX))
is always true after login flow.
答案2
得分: 0
首先,在身份验证过滤器中生成并设置令牌,将其放在HttpServletResponse头部而不是请求对象的头部。然后,授权过滤器检查请求头中的令牌,所以可能会出现null
的问题。
通常,身份验证和授权不会像这样链式操作,但不清楚您尝试实现的实际用例。
英文:
First thing, in the authentication filter token generated and set on the HttpServletResponse header not on the request object's header. Then next the authorization filter checking the request header for token, so there may be the issue of null
happened.
Usually authentication and authorization will not be chained like this, but don't know regarding the actual use case you were trying to implement.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论