JWT身份验证与Spring和Angular中的空标头

huangapple go评论87阅读模式
英文:

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 -&gt; new CorsConfiguration().applyPermitDefaultValues())
        .and().csrf().disable()
  	            .authorizeRequests()                                                                
				.antMatchers(&quot;/**&quot;).permitAll()                  
				.antMatchers(&quot;/manage/**&quot;).hasRole(&quot;ADMIN&quot;)                                      
				.antMatchers(&quot;/&quot;).hasRole(&quot;USER&quot;)
				.and()
				.exceptionHandling()
	            .accessDeniedPage(&quot;/access-denied&quot;)
	            .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, &quot;&quot;))
                .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&lt;GrantedAuthority&gt; 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(() -&gt; new UsernameNotFoundException(&quot;User not found&quot;));
		List&lt;GrantedAuthority&gt; authorityListAdmin = AuthorityUtils.createAuthorityList(&quot;ROLE_ADMIN&quot;);
		List&lt;GrantedAuthority&gt; authorityListUser = AuthorityUtils.createAuthorityList(&quot;ROLE_USER&quot;);
		if (user.getRole() == &quot;admin&quot;) {
			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 = &quot;/login&quot;)
public ModelAndView login(
        @RequestParam(name = &quot;error&quot;, required = false) String error,
	    @RequestParam(name = &quot;logout&quot;, required = false) String logout,
	    HttpServletRequest request,
	    HttpServletResponse response) {

    ModelAndView mv = new ModelAndView(&quot;login&quot;);
	HttpSession session= request.getSession(false);
	Authentication auth = SecurityContextHolder.getContext()
	    .getAuthentication();
      
	System.out.println(&quot;auth ===&quot; + auth);
	System.out.println(&quot;logout ===&quot; + 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.

huangapple
  • 本文由 发表于 2020年7月29日 20:43:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/63153902.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定