java - Rest API 中的 JWT token 传递

标签 java spring hibernate spring-boot spring-security

我正在尝试实现基于 JWT token 的身份验证授权。我使用 Spring Boot 作为后端,使用 Angular 7 作为前端,我的工作是完成后端工作。认证中已成功生成 Bearer Token。我已将其添加到 header 中,但当我尝试使用 request.getHeader(HEADER_STRING) 获取 header 时,它是null

那么如何使用这个生成的 token 来进一步识别登录用户,或者在 token 生成后由前端工作来识别用户?

我在 Spring Security 中使用了自定义登录页面,当我向 http://localhost:8080/login 发出请求时,它包含两个登录表单,而不是一个。

Image

控制台

doFilterInternal header null response.getHeader(HEADER_STRING) null
getAuthenticationToken token: null
doFilterInternal authenticationToken : null null
doFilterInternal header null response.getHeader(HEADER_STRING) null
getAuthenticationToken token: null
doFilterInternal authenticationToken : null null
doFilterInternal header null response.getHeader(HEADER_STRING) null
getAuthenticationToken token: null
doFilterInternal authenticationToken : null null
doFilterInternal header null response.getHeader(HEADER_STRING) null
getAuthenticationToken token: null
doFilterInternal authenticationToken : null null
attemptAuthentication  email: <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="394b49795e54585055175a5654" rel="noreferrer noopener nofollow">[email protected]</a> abcd@A123
2019-03-04 11:33:34.320  INFO 11652 --- [nio-8080-exec-5] o.h.h.i.QueryTranslatorFactoryInitiator  : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select user0_.id as id1_1_, user0_.branch as branch2_1_, user0_.contact as contact3_1_, user0_.createtime as createti4_1_, user0_.designation as designat5_1_, user0_.email as email6_1_, user0_.expreiance as expreian7_1_, user0_.name as name8_1_, user0_.password as password9_1_, user0_.role as role10_1_, user0_.skypeid as skypeid11_1_, user0_.statusenable as statuse12_1_ from user user0_ where user0_.email=?
loadUserByUsername User [user.toString] Role: ROLE_ADMIN
successfulAuthentication  username <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b9cbc9f9ded4d8d0d597dad6d4" rel="noreferrer noopener nofollow">[email protected]</a>
successfulAuthentication bearer Token Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJycEBjeWduZXRpbmZvdGVjaC5jb20iLCJleHAiOjE1NTE3NjU4MTR9.9mLS64W6JBS1RqlEKl1Zmjb8YS03E9k92ITkaFmw35JH4ELIua8Tbkzj0r9crDgdQnxm3YvFKAD9lY3cgoQsNw
doFilterInternal header null response.getHeader(HEADER_STRING) null
getAuthenticationToken token: null
doFilterInternal authenticationToken : null null
doFilterInternal header null response.getHeader(HEADER_STRING) null
getAuthenticationToken token: null
doFilterInternal authenticationToken : null null
doFilterInternal header null response.getHeader(HEADER_STRING) null
getAuthenticationToken token: null
doFilterInternal authenticationToken : null null

JWTAuthenticationFilter.java

public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    private AuthenticationManager authenticationManager;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Autowired
    CustomUserDetailService customUserDetailService;

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            System.out.println("attemptAuthentication "+" email: "+request.getParameter("email") + " "+request.getParameter("password"));
            //User user = new ObjectMapper().readValue(request.getInputStream(), User.class);
            return this.authenticationManager
                    .authenticate(new UsernamePasswordAuthenticationToken(request.getParameter("email"), request.getParameter("password")));
        } catch (Exception 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();
        System.out.println("successfulAuthentication "+" username "+username);

        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("successfulAuthentication bearer Token "+bearerToken);

        response.getWriter().write(bearerToken);
        response.addHeader(HEADER_STRING, bearerToken);
        response.sendRedirect(SIGN_UP_SUCCESS);
    }
}

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);

        System.out.println("doFilterInternal header "+header+ " response.getHeader(HEADER_STRING) "+response.getHeader(HEADER_STRING));
        if (header == null || !header.startsWith(TOKEN_PREFIX)) {
            chain.doFilter(request, response);
        }
            UsernamePasswordAuthenticationToken authenticationToken = getAuthenticationToken(request);
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            response.addHeader(header, SIGN_UP_URL);
            System.out.println("doFilterInternal authenticationToken : "+authenticationToken+ " "+response.getHeader(SIGN_UP_URL));
            chain.doFilter(request, response);


    }

    private UsernamePasswordAuthenticationToken getAuthenticationToken(HttpServletRequest request) {
        String token = request.getHeader(HEADER_STRING);
        System.out.println("getAuthenticationToken token: "+token);
        if (token == null) return null;
        String username = Jwts.parser().setSigningKey(SECRET)
                .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                .getBody()
                .getSubject();
        UserDetails userDetails = customUserDetailService.loadUserByUsername(username);
        System.out.println("getAuthenticationToken userDetails "+userDetails.toString()+ " userDetails.getAuthorities() "+userDetails.getAuthorities());
        return username != null ?
                new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()) : null;
    }
}

CustomUserDetailService.java

@Component
public class CustomUserDetailService implements UserDetailsService {
    private final UserRepository userRepository;

    @Autowired
    public CustomUserDetailService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByEmail(username);

                if(user==null) 
                    {
                        new UsernameNotFoundException("User not found");
                        return null;    
                    }
                else
                {
                    System.out.println("loadUserByUsername "+user.toString()+" Role: "+user.getRole());
                    List<GrantedAuthority> authorityListAdmin = AuthorityUtils.createAuthorityList("ROLE_USER", "ROLE_ADMIN");
                    List<GrantedAuthority> authorityListUser = AuthorityUtils.createAuthorityList("ROLE_USER");
                    return new org.springframework.security.core.userdetails.User
                            (user.getEmail(), user.getPassword(), user.getRole()=="ADMIN" ? authorityListAdmin : authorityListUser);

                }
    }
}

SecuriyConfig.java

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer{

     @Autowired
     private CustomUserDetailService customUserDetailService;


    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

     @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(customUserDetailService).passwordEncoder(new BCryptPasswordEncoder());
        }


    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/home").setViewName("home");
        registry.addViewController("/").setViewName("login");
        registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
        registry.addViewController("/login").setViewName("login");
        registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
        registry.addViewController("/failure").setViewName("failure");
        registry.addViewController("/403").setViewName("403");
    }

        @Override
        protected void configure(HttpSecurity http) throws Exception{

             http
             .csrf().disable()
             .authorizeRequests().antMatchers("/login","/home","/failure").permitAll()
             .antMatchers(HttpMethod.POST,"/admin/**").permitAll()//hasRole("ADMIN") 
             .antMatchers(HttpMethod.PUT,"/admin/**").hasRole("ADMIN")
             .antMatchers(HttpMethod.GET,"/admin/**").hasRole("ADMIN")
             .antMatchers(HttpMethod.GET,"/user/**").hasAnyRole("ADMIN","USER")
             .antMatchers(HttpMethod.POST,"/user/**").hasAnyRole("ADMIN","USER")
             .anyRequest().authenticated()
             .and()
             .addFilter(new JWTAuthenticationFilter(authenticationManager()))
             .addFilter(new JWTAuthorizationFilter(authenticationManager(), customUserDetailService))
             .exceptionHandling().accessDeniedPage("/403")
             .and()
             .formLogin()
             .loginPage("/login")
             .loginProcessingUrl("/login")
             .usernameParameter("email")
             .passwordParameter("password")
             .defaultSuccessUrl("/home",true)
             .failureUrl("/failure")
             .and()
             .logout().logoutUrl("/logout").permitAll();

    }

        public SecurityConfig(UserDetailsService userDetailsService) {
            super();
            this.customUserDetailService = customUserDetailService;
        }       
}   

SecurityConstants.java

public class SecurityConstants {
    static final String SECRET = "Romil";
    static final String TOKEN_PREFIX = "Bearer ";
    static final String HEADER_STRING = "Authorization";
    static final String SIGN_UP_URL = "/login";
    static final String SIGN_UP_SUCCESS = "/home";
    static final long EXPIRATION_TIME = 86400000L;
}

最佳答案

如何传递Token?

  • 成功登录后在 Spring Boot 中生成 token ,将 token 发送为 响应前端
  • 在 Angular 中创建 AuthInterceptor,它将在每个 进一步请求。

注释:

  1. 我们还必须根据我们的要求设置WebConfig文件。

    • 允许的方法
    • 允许的 header
    • 暴露的 header
    • 最大年龄等..
  2. 对于不需要对用户进行身份验证和授权的请求,我们可以在 ignoring().antMatchers("") 中添加该 API .

  @Override
      public void configure(WebSecurity web) throws Exception {
          web
            .ignoring()
              .antMatchers("/userlogin/")
              .antMatchers("/forgetPassword");
      }

为什么要访问控制公开 header ?

Access-Control-Expose-Headers(可选)- XMLHttpRequest 对象有一个 getResponseHeader() 方法,该方法返回特定响应 header 的值。在CORS request期间,getResponseHeader() method can only access simple response headers 。简单的响应头定义如下:

  • 缓存控制
  • 内容语言
  • 内容类型
  • 过期
  • 最后修改时间
  • 编译指示

如果您希望客户端能够访问其他 header ,则必须使用 Access-Control-Expose-Headers header 。此 header 的值是要向客户端公开的响应 header 的逗号分隔列表。

response.setHeader("Access-Control-Expose-Headers", "Authorization");

AuthInterceptor

import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = window.localStorage.getItem('tokenKey'); // you probably want to store it in localStorage or something


    if (!token) {
      return next.handle(req);
    }

    const req1 = req.clone({
      headers: req.headers.set('Authorization', `${token}`),
    });

    return next.handle(req1);
  }

}

关于java - Rest API 中的 JWT token 传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54977421/

相关文章:

Java Hibernate EntityManager 同步

java - Spring Security 和 CAS 集成

java - CXF客户端异常: Interceptor for {XXX} has thrown exception,现在展开

java - Spring - 如何将组件从另一个模块注入(inject)到 SpringBoot 应用程序中

java - 从 @EmbeddedId 类中提取父类(super class)时,实体没有持久性 id 属性

sql - 如何在 Hibernate Native SQL Query 中急切获取连接表集合?

java - 星火 2.0.1 java.lang.NegativeArraySizeException

java - 移动服务器时 JSP java.lang.NullPointerException

java - 以静默方式将作业返回到队列

java - WebSecurityConfigurerAdapter 不起作用