0

大家好,我对 Java 的 jwt 有疑问。这里是代码。这是邮递员的返回值

{
    "timestamp": "2020-02-29T20:53:35.761+0000",
    "status": 403,
    "error": "Forbidden",
    "message": "Access Denied",
    "path": "/login"
}

令牌管理器.java


@Service
public class TokenManager {
    private static final int expiredAt = 10 * 60 * 60 * 1000;
    Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
    public String generateToken(String username){


         return Jwts.builder().setSubject(username)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + expiredAt))
                .signWith(key).compact();
    }

    public boolean tokenValidate(String token){
        if(getUserFromToken(token) != null &&  isExpired(token)) {
            return true;
        }
        return false;
    }

    public String getUserFromToken(String token){
        Claims claims = getClaims(token);
        return claims.getSubject();
    }

    public boolean isExpired(String token){
        Claims claims = getClaims(token);
        return claims.getExpiration().after(new Date(System.currentTimeMillis()));
    }

    private Claims getClaims(String token) {
        return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
    }
} 

然后是 JwtTokenFilter.java

@Component
public class JwtTokenFilter extends OncePerRequestFilter {
    @Autowired
    private TokenManager tokenManager;
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest,
                                    @NotNull HttpServletResponse httpServletResponse,
                                    @NotNull FilterChain filterChain) throws ServletException, IOException {


        final String authHeader = httpServletRequest.getHeader("Authorization");

        String username = null;
        String token = null;


        if (authHeader != null && authHeader.contains("Bearer")) {
            token = authHeader.substring(7);
            try {
                username = tokenManager.getUserFromToken(token);
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }

        if (username != null && token != null
                && SecurityContextHolder.getContext().getAuthentication() == null) {
            if (tokenManager.tokenValidate(token)) {
                UsernamePasswordAuthenticationToken upassToken =
                        new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
                upassToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
                SecurityContextHolder.getContext().setAuthentication(upassToken);
            }
        }

        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}

And my custom UserDetailService

@Service
public class CustomUserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {

    @Autowired
    private UserRepository userRepository;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userRepository.findByUsername(username);
    }
}

这是 WebSecurityConfig

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private JwtTokenFilter tokenFilter;
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable()
                .authorizeRequests().antMatchers("/signup","/login").permitAll()
                .anyRequest().authenticated();


        http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);

    }

    @Bean
    public AuthenticationManager getAuthenticationManager() throws Exception {
        return super.authenticationManagerBean();
    }

}

最后一个是我的控制器。我检查了请求正文并打印了它工作正常但 /login 路径返回访问被拒绝的数据。

@RestController
public class UserController {
    @Autowired
    private UserService userService;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private TokenManager tokenManager;

    public UserController(UserService userService, AuthenticationManager authenticationManager, TokenManager tokenManager) {
        this.userService = userService;
        this.authenticationManager = authenticationManager;
        this.tokenManager = tokenManager;
    }

    @RequestMapping(value = "/signup", method = RequestMethod.POST)
    public ResponseEntity<User> signup(@RequestBody User user){
        return ResponseEntity.ok(userService.save(user));
    }

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public ResponseEntity<String> login(@Valid @RequestBody AuthRequest authRequest){
        try{
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authRequest.getUsername(),authRequest.getPassword()));
            return ResponseEntity.ok(tokenManager.generateToken(authRequest.getUsername()));
        }catch (Exception e){
            throw e;
        }
    }

}

当我在登录函数中删除 authenticationManager.authenticate 方法时,它返回一个有效的令牌。但是当我再次添加 authenticationManager 时,它返回访问被拒绝。

4

1 回答 1

0

其实你没有AuthenticationManager正确设置。

在您的代码中,您只是使用了默认的身份验证管理器。没关系,因为 Spring 引导安全中提供了一个默认实现,即ProviderManager. 什么[ProviderManager][1]是:

通过AuthenticationProviders.

所以你至少需要一个AuthenticationProvider

有很多 AuthenticationProviders,例如:

AnonymousAuthenticationProvider, NullAuthenticationProvider,等DaoAuthenticationProvider_LdapAuthenticationProvider

在您的情况下,您正在对数据库进行身份验证,因此这DaoAuthenticationProvider是选择。

Spring security 有一种非常简单的方法来配置DaoAuthenticationProvider,实际上,当您将userDetailsS​​ervice设置为AuthenticationManagerBuilder来配置您的 时,它会自动为您创建一个AuthenticationManager,代码如下:


    @Autowired
    private CustomUserDetailsService userDetailsService;

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

所以您需要做的就是将上面的代码片段添加到您的 WebSecurityConfig

并且还建议使用 PasswordEncoder 而不是将密码存储为纯文本。一种简单的方法是在将用户保存到数据库之前使用 BCryptPasswordEncoder 对密码进行编码...


    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
于 2020-03-13T07:03:37.623 回答