Spring Boot JWT возвращает отказано в доступе - PullRequest
0 голосов
/ 01 марта 2020

Привет всем, у меня проблемы с JWT с Java. Вот коды. Вот возвращаемое значение от почтальона

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

TokenManager. 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();
    }

}

И, наконец, мой контроллер. Я проверил тело запроса и распечатал данные, он просто отлично работает, но / путь входа возвращает отказано в доступе.

@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 внутри функции входа в систему, он возвращает действительный токен. Но когда я снова добавляю authenticManager, он возвращает отказ в доступе.

1 Ответ

0 голосов
/ 13 марта 2020

На самом деле вы не правильно настроили AuthenticationManager.

в своем коде, вы просто использовали менеджер аутентификации по умолчанию. И это нормально, так как в Spring boot security поставлена ​​одна реализация по умолчанию, ProviderManager. [ProviderManager][1] делает:

. Итерирует запрос аутентификации в списке AuthenticationProviders.

Так что вам нужен хотя бы один AuthenticationProvider

Есть довольно много AuthenticationProviders, например:

AnonymousAuthenticationProvider, NullAuthenticationProvider, DaoAuthenticationProvider, LdapAuthenticationProvider et c

А в вашем случае вы проходите аутентификацию на базе данных, поэтому DaoAuthenticationProvider - это выбор.

А у Spring Spring очень простой способ настроить DaoAuthenticationProvider, и фактически он автоматически создан для вас, когда вы установите userDetailsService на AuthenticationManagerBuilder для настройки AuthenticationManager, код такой:


    @Autowired
    private CustomUserDetailsService userDetailsService;

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

так что все, что вам нужно сделать, это добавить приведенный выше фрагмент кода в ваш WebSecurityConfig

И также рекомендуется использовать PasswordEncoder вместо хранения вашего пароля в виде простого текста. Простой способ - использовать BCryptPasswordEncoder для кодирования вашего пароля перед сохранением пользователя в db ...


    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
...