0

Hey everyone i have problem with jwt with Java.Here is the codes. Here is returned value from postman

{
    "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();
    }
} 

And then 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);
    }
}

Here is 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();
    }

}

And last one is my controller.I checked the request body and and print the data it just work fine but /login path returns access denied.

@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;
        }
    }

}

When I remove authenticationManager.authenticate method inside login function it returns a valid token.But when I add authenticationManager again it returns access denied.

isa_toltar
  • 586
  • 5
  • 11

1 Answers1

0

Actually you did not setup the AuthenticationManager properly.

in your code, you just used the default authentication manager. And it is ok, as there is one default implementation shipped in Spring boot security, which is ProviderManager. what [ProviderManager][1] does is:

Iterates an Authentication request through a list of AuthenticationProviders.

So you need at least one AuthenticationProvider

There are quite some AuthenticationProviders, for example:

AnonymousAuthenticationProvider, NullAuthenticationProvider, DaoAuthenticationProvider, LdapAuthenticationProvider etc

And in your case, you are authenticating against database, so the DaoAuthenticationProvider is the choice.

And Spring security has a very easy way to configure the DaoAuthenticationProvider, and actually, it automatically created one for you when you set userDetailsService to the AuthenticationManagerBuilder to configure your AuthenticationManager, code like this:


    @Autowired
    private CustomUserDetailsService userDetailsService;

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

so all you need to do is add the code snipet above to your WebSecurityConfig

And it is also recommended to use PasswordEncoder instead of storing your password as plain text. A simple way is to use BCryptPasswordEncoder to encode your password before save the user to 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();
    }
bob tang
  • 583
  • 3
  • 12