2

I have a Spring 5 MVC/REST application. i using JPA Hibernate as the ORM. I want to secure my application. All examples I see are based on Spring Boot. I want use jwt in my application. I want without XML Config too.

( Spring MVC - 5.2.0.RELEASE )

I need Example. Thanks

4
  • Is this what you are looking for ?: baeldung.com/spring-security-oauth-jwt Commented Jan 25, 2020 at 16:06
  • thanks dear @R.G but I want example without Spring boot Commented Jan 26, 2020 at 4:44
  • @mohammad_soleimani did u manage to find the solution without spring boot. Commented Jun 9, 2020 at 6:57
  • dear @oOXAam , there is a example in GitHub now Commented Jan 23, 2021 at 11:48

2 Answers 2

2

To secure a Spring REST application using Spring Security and JWT, you can follow the below steps

  1. Add the following dependencies in pom.xml

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>5.2.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
        <version>5.2.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
    
  2. Create JwtAuthenticationEntryPoint to rejet every unauthenticated request

    @Component
    public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }
    }
    
  3. Create model classes

    public class JwtRequest implements Serializable {
        private static final long serialVersionUID = 5926468583005150707L;
        private String username;
        private String password;
    
        // need default constructor for JSON Parsing
        public JwtRequest() {
        }
    
        public JwtRequest(String username, String password) {
            this.setUsername(username);
            this.setPassword(password);
        }
    // Getters and Setters
    }
    
    public class JwtResponse implements Serializable {
        private static final long serialVersionUID = -8091879091924046844L;
        private final String jwttoken;
    
        public JwtResponse(String jwttoken) {
            this.jwttoken = jwttoken;
        }
    
        public String getToken() {
            return this.jwttoken;
        }
    }
    
  4. Create Util class for token generation and validation

    @Component
    public class JwtTokenUtil implements Serializable {
        private static final long serialVersionUID = -2550185165626007488L;
        public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60;
        @Value("${jwt.secret}")
        private String secret;
    
        // retrieve username from jwt token
        public String getUsernameFromToken(String token) {
            return getClaimFromToken(token, Claims::getSubject);
        }
    
        // retrieve expiration date from jwt token
        public Date getExpirationDateFromToken(String token) {
            return getClaimFromToken(token, Claims::getExpiration);
        }
    
        public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
            final Claims claims = getAllClaimsFromToken(token);
            return claimsResolver.apply(claims);
        }
    
        // for retrieveing any information from token we will need the secret key
        private Claims getAllClaimsFromToken(String token) {
            return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        }
    
        // check if the token has expired
        private Boolean isTokenExpired(String token) {
            final Date expiration = getExpirationDateFromToken(token);
            return expiration.before(new Date());
        }
    
        // generate token for user
        public String generateToken(UserDetails userDetails) {
            Map<String, Object> claims = new HashMap<>();
            return doGenerateToken(claims, userDetails.getUsername());
        }
    
        // while creating the token -
        // 1. Define claims of the token, like Issuer, Expiration, Subject, and the ID
        // 2. Sign the JWT using the HS512 algorithm and secret key.
        // 3. According to JWS Compact
        // Serialization(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1)
        // compaction of the JWT to a URL-safe string
        private String doGenerateToken(Map<String, Object> claims, String subject) {
            return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
                    .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000)).signWith(SignatureAlgorithm.HS512, secret).compact();
        }
    
        // validate token
        public Boolean validateToken(String token, UserDetails userDetails) {
            final String username = getUsernameFromToken(token);
            return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
        }
    }
    
  5. Create UserDetailsService and UserRepository implementations to load the user by user name from database

    @Service
    public class JwtUserDetailsService implements UserDetailsService {
    
        @Autowired
        private UserRepository repo;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            UserEntity user = repo.findByUsername(username);
            if (user != null) {
                return new User(user.getUsername(), user.getPassword(), new ArrayList<>());
            } else {
                throw new UsernameNotFoundException("User not found with username: " + username);
            }
        }
    }
    
  6. Create a reqeust filter to check if the request has a valid JWT token. If it has a valid JWT Token then set the Authentication in the context, to specify that the current user is authenticated.

    @Component
    public class JwtRequestFilter extends OncePerRequestFilter {
    @Autowired
    private JwtUserDetailsService jwtUserDetailsService;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        final String requestTokenHeader = request.getHeader("Authorization");
        String username = null;
        String jwtToken = null;
        // JWT Token is in the form "Bearer token". Remove Bearer word and get
        // only the Token
        if (requestTokenHeader != null &amp;&amp; requestTokenHeader.startsWith("Bearer ")) {
            jwtToken = requestTokenHeader.substring(7);
            try {
                username = jwtTokenUtil.getUsernameFromToken(jwtToken);
            } catch (IllegalArgumentException e) {
                System.out.println("Unable to get JWT Token");
            } catch (ExpiredJwtException e) {
                System.out.println("JWT Token has expired");
            }
        } else {
            logger.warn("JWT Token does not begin with Bearer String");
        }
        // Once we get the token validate it.
        if (username != null &amp;&amp; SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
            // if token is valid configure Spring Security to manually set
            // authentication
            if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                // After setting the Authentication in the context, we specify
                // that the current user is authenticated. So it passes the
                // Spring Security Configurations successfully.
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            }
        }
        chain.doFilter(request, response);
    }
    }
    
  7. Create an authentication controller to expose a POST API /authenticate which accepts username and password in the body. Using Spring Authentication Manager we authenticate the username and password. If the credentials are valid, a JWT token is created using the JwtTokenUtil and provided to the client.

        @RestController
        @CrossOrigin
        public class JwtAuthenticationController {
        @Autowired
        private AuthenticationManager authenticationManager;
        @Autowired
        private JwtTokenUtil jwtTokenUtil;
        @Autowired
        private JwtUserDetailsService userDetailsService;
    
        @PostMapping("/authenticate")
        public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtRequest authenticationRequest) throws Exception {
            authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
            final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
            final String token = jwtTokenUtil.generateToken(userDetails);
            return ResponseEntity.ok(new JwtResponse(token));
        }
    
        private void authenticate(String username, String password) throws Exception {
            try {
                authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
            } catch (DisabledException e) {
                throw new Exception("USER_DISABLED", e);
            } catch (BadCredentialsException e) {
                throw new Exception("INVALID_CREDENTIALS", e);
            }
        }
        }       
    
  8. Configure Spring Security

    @Configuration
    @ComponentScan(basePackages = "com.javachinna")
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    @Autowired
    private UserDetailsService jwtUserDetailsService;
    @Autowired
    private JwtRequestFilter jwtRequestFilter;
    
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        // configure AuthenticationManager so that it knows from where to load
        // user for matching credentials
        // Use BCryptPasswordEncoder
        auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        // We don't need CSRF for this example
        httpSecurity.csrf().disable()
                // dont authenticate this particular request
                .authorizeRequests().antMatchers("/authenticate").permitAll().
                // all other requests need to be authenticated
                anyRequest().authenticated().and().
                // make sure we use stateless session; session won't be used to
                // store user's state.
                exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // Add a filter to validate the tokens with every request
        httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }
    }
    
  9. Create SecurityWebApplicationInitializer to register the springSecurityFilterChain

    public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
    
    }
    
  10. Add the WebSecurityConfig class to the existing ApplicationInitializer

    public class SpringWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class[] getRootConfigClasses() {
        return new Class[] { WebSecurityConfig.class };
    }
    // ... other overrides ...
    }
    
  11. Add the secret key to the application.properties file

    App Properties

    jwt.secret=javachinna

That's it. Now generate a JSON Web Token by creating a POST request with url localhost:8080/authenticate and valid username and password in the request body. Use this token in the request header for each request.

For more information, refer the article on Securing Spring REST services using JWT without using spring boot.

Sign up to request clarification or add additional context in comments.

2 Comments

While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From Review
I've included the essential parts of the answer here and provided the link for reference.
0

I write a sample for use Spring security 5.0.0 none boot and with JWT, dynamic Role. I hope it's good

  1. Spring MVC 5.0.0
  2. Spring Security 5.0.0
  3. hibernate JPA 5.2.11
  4. and my DB is Oracle DB

you can download it : download from github

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.