Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit ad2af00

Browse files
Initial commit
0 parents commit ad2af00

31 files changed

+1071
-0
lines changed

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# spring-boot-rest-jwt-auth
2+
Secure Spring Boot 2 REST API with Spring Security 5 JWT Authentication, Role based Authorization and MySQL Database

‎pom.xml‎

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>org.springframework.boot</groupId>
7+
<artifactId>spring-boot-starter-parent</artifactId>
8+
<version>2.2.6.RELEASE</version>
9+
<relativePath /> <!-- lookup parent from repository -->
10+
</parent>
11+
<groupId>com.javachinna</groupId>
12+
<artifactId>spring-boot-rest-crud</artifactId>
13+
<version>0.0.1-SNAPSHOT</version>
14+
<name>spring-boot-rest-crud</name>
15+
<description>Demo project for Spring Boot REST API CRUD Operations with Swagger Documentation</description>
16+
17+
<properties>
18+
<java.version>11</java.version>
19+
</properties>
20+
21+
<dependencies>
22+
<dependency>
23+
<groupId>org.springframework.boot</groupId>
24+
<artifactId>spring-boot-starter-data-jpa</artifactId>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.springframework.boot</groupId>
28+
<artifactId>spring-boot-starter-web</artifactId>
29+
</dependency>
30+
<dependency>
31+
<groupId>org.springframework.boot</groupId>
32+
<artifactId>spring-boot-starter-security</artifactId>
33+
</dependency>
34+
<dependency>
35+
<groupId>org.springframework.boot</groupId>
36+
<artifactId>spring-boot-devtools</artifactId>
37+
<scope>runtime</scope>
38+
<optional>true</optional>
39+
</dependency>
40+
<dependency>
41+
<groupId>mysql</groupId>
42+
<artifactId>mysql-connector-java</artifactId>
43+
<scope>runtime</scope>
44+
</dependency>
45+
<dependency>
46+
<groupId>org.projectlombok</groupId>
47+
<artifactId>lombok</artifactId>
48+
<optional>true</optional>
49+
</dependency>
50+
<dependency>
51+
<groupId>org.springframework.boot</groupId>
52+
<artifactId>spring-boot-starter-test</artifactId>
53+
<scope>test</scope>
54+
<exclusions>
55+
<exclusion>
56+
<groupId>org.junit.vintage</groupId>
57+
<artifactId>junit-vintage-engine</artifactId>
58+
</exclusion>
59+
</exclusions>
60+
</dependency>
61+
<dependency>
62+
<groupId>org.springframework.security</groupId>
63+
<artifactId>spring-security-test</artifactId>
64+
<scope>test</scope>
65+
</dependency>
66+
<dependency>
67+
<groupId>io.springfox</groupId>
68+
<artifactId>springfox-swagger2</artifactId>
69+
<version>2.9.2</version>
70+
</dependency>
71+
<dependency>
72+
<groupId>io.springfox</groupId>
73+
<artifactId>springfox-swagger-ui</artifactId>
74+
<version>2.9.2</version>
75+
</dependency>
76+
<dependency>
77+
<groupId>io.jsonwebtoken</groupId>
78+
<artifactId>jjwt</artifactId>
79+
<version>0.9.1</version>
80+
</dependency>
81+
</dependencies>
82+
83+
<build>
84+
<plugins>
85+
<plugin>
86+
<groupId>org.springframework.boot</groupId>
87+
<artifactId>spring-boot-maven-plugin</artifactId>
88+
</plugin>
89+
</plugins>
90+
</build>
91+
92+
</project>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.javachinna;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class SpringBootRestCrudApplication {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(SpringBootRestCrudApplication.class, args);
11+
}
12+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.javachinna.config;
2+
3+
import java.lang.annotation.Documented;
4+
import java.lang.annotation.ElementType;
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
import java.lang.annotation.Target;
8+
9+
@Documented
10+
@Retention(RetentionPolicy.RUNTIME)
11+
@Target(ElementType.TYPE)
12+
public @interface AddApiToSwagger {
13+
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.javachinna.config;
2+
3+
import java.lang.annotation.Documented;
4+
import java.lang.annotation.ElementType;
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
import java.lang.annotation.Target;
8+
9+
@Documented
10+
@Retention(RetentionPolicy.RUNTIME)
11+
@Target(ElementType.METHOD)
12+
public @interface AddMethodToSwagger {
13+
14+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.javachinna.config;
2+
3+
import javax.sql.DataSource;
4+
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.context.annotation.Bean;
7+
import org.springframework.context.annotation.Configuration;
8+
import org.springframework.context.annotation.Profile;
9+
import org.springframework.http.HttpMethod;
10+
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
11+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
12+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
13+
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
14+
import org.springframework.security.config.http.SessionCreationPolicy;
15+
import org.springframework.security.core.userdetails.UserDetailsService;
16+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
17+
import org.springframework.security.crypto.password.PasswordEncoder;
18+
19+
import com.javachinna.model.Role;
20+
21+
@Profile(Profiles.BASIC_AUTH)
22+
@Configuration
23+
@EnableWebSecurity
24+
public class BasicAuthSecurityConfig extends WebSecurityConfigurerAdapter {
25+
26+
@Autowired
27+
private UserDetailsService userDetailsService;
28+
29+
@Autowired
30+
DataSource dataSource;
31+
32+
@Autowired
33+
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
34+
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
35+
}
36+
37+
@Override
38+
protected void configure(HttpSecurity http) throws Exception {
39+
// Disable CSRF
40+
http.csrf().disable()
41+
// Only admin can perform HTTP delete operation
42+
.authorizeRequests().antMatchers(HttpMethod.DELETE).hasRole(Role.ADMIN)
43+
// any authenticated user can perform all other operations
44+
.antMatchers("/products/**").hasAnyRole(Role.ADMIN, Role.USER).and().httpBasic()
45+
// Permit all other request without authentication
46+
.and().authorizeRequests().anyRequest().permitAll()
47+
// We don't need sessions to be created.
48+
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
49+
}
50+
51+
@Override
52+
public UserDetailsService userDetailsService() {
53+
return userDetailsService;
54+
}
55+
56+
@Bean
57+
public PasswordEncoder passwordEncoder() {
58+
return new BCryptPasswordEncoder(10);
59+
}
60+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.javachinna.config;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.context.annotation.Bean;
5+
import org.springframework.context.annotation.Configuration;
6+
import org.springframework.context.annotation.Profile;
7+
import org.springframework.http.HttpMethod;
8+
import org.springframework.security.authentication.AuthenticationManager;
9+
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
10+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
11+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
12+
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
13+
import org.springframework.security.config.http.SessionCreationPolicy;
14+
import org.springframework.security.core.userdetails.UserDetailsService;
15+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
16+
import org.springframework.security.crypto.password.PasswordEncoder;
17+
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
18+
19+
import com.javachinna.model.Role;
20+
21+
import lombok.RequiredArgsConstructor;
22+
23+
@Profile(Profiles.JWT_AUTH)
24+
@Configuration
25+
@EnableWebSecurity
26+
@RequiredArgsConstructor
27+
public class JwtAuthSecurityConfig extends WebSecurityConfigurerAdapter {
28+
29+
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
30+
private final UserDetailsService jwtUserDetailsService;
31+
private final JwtRequestFilter jwtRequestFilter;
32+
33+
@Autowired
34+
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
35+
// configure AuthenticationManager so that it knows from where to load
36+
// user for matching credentials
37+
// Use BCryptPasswordEncoder
38+
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
39+
}
40+
41+
@Bean
42+
public PasswordEncoder passwordEncoder() {
43+
return new BCryptPasswordEncoder();
44+
}
45+
46+
@Bean
47+
@Override
48+
public AuthenticationManager authenticationManagerBean() throws Exception {
49+
return super.authenticationManagerBean();
50+
}
51+
52+
@Override
53+
protected void configure(HttpSecurity httpSecurity) throws Exception {
54+
// Disable CSRF
55+
httpSecurity.csrf().disable()
56+
// Only admin can perform HTTP delete operation
57+
.authorizeRequests().antMatchers(HttpMethod.DELETE).hasRole(Role.ADMIN)
58+
// any authenticated user can perform all other operations
59+
.antMatchers("/products/**").hasAnyRole(Role.ADMIN, Role.USER).and().httpBasic()
60+
// Permit all other request without authentication
61+
.and().authorizeRequests().anyRequest().permitAll()
62+
// Reject every unauthenticated request and send error code 401.
63+
.and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
64+
// We don't need sessions to be created.
65+
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
66+
67+
// Add a filter to validate the tokens with every request
68+
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
69+
}
70+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.javachinna.config;
2+
3+
import java.io.IOException;
4+
import java.io.Serializable;
5+
6+
import javax.servlet.http.HttpServletRequest;
7+
import javax.servlet.http.HttpServletResponse;
8+
9+
import org.springframework.security.core.AuthenticationException;
10+
import org.springframework.security.web.AuthenticationEntryPoint;
11+
import org.springframework.stereotype.Component;
12+
13+
@Component
14+
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
15+
private static final long serialVersionUID = -7858869558953243875L;
16+
17+
@Override
18+
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
19+
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
20+
}
21+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.javachinna.config;
2+
3+
import java.io.IOException;
4+
5+
import javax.servlet.FilterChain;
6+
import javax.servlet.ServletException;
7+
import javax.servlet.http.HttpServletRequest;
8+
import javax.servlet.http.HttpServletResponse;
9+
10+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
11+
import org.springframework.security.core.context.SecurityContextHolder;
12+
import org.springframework.security.core.userdetails.UserDetails;
13+
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
14+
import org.springframework.stereotype.Component;
15+
import org.springframework.web.filter.OncePerRequestFilter;
16+
17+
import com.javachinna.service.UserDetailsServiceImpl;
18+
import com.javachinna.util.JwtTokenUtil;
19+
20+
import io.jsonwebtoken.ExpiredJwtException;
21+
import lombok.RequiredArgsConstructor;
22+
23+
@Component
24+
@RequiredArgsConstructor
25+
public class JwtRequestFilter extends OncePerRequestFilter {
26+
private final UserDetailsServiceImpl jwtUserDetailsService;
27+
private final JwtTokenUtil jwtTokenUtil;
28+
29+
@Override
30+
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
31+
final String requestTokenHeader = request.getHeader("Authorization");
32+
String username = null;
33+
String jwtToken = null;
34+
// JWT Token is in the form "Bearer token". Remove Bearer word and get
35+
// only the Token
36+
if (requestTokenHeader != null) {
37+
if (requestTokenHeader.startsWith("Bearer ")) {
38+
jwtToken = requestTokenHeader.substring(7);
39+
try {
40+
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
41+
} catch (IllegalArgumentException e) {
42+
System.out.println("Unable to get JWT Token");
43+
} catch (ExpiredJwtException e) {
44+
System.out.println("JWT Token has expired");
45+
}
46+
} else {
47+
logger.warn("JWT Token does not begin with Bearer String");
48+
}
49+
}
50+
// Once we get the token validate it.
51+
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
52+
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
53+
// if token is valid configure Spring Security to manually set
54+
// authentication
55+
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
56+
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
57+
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
58+
// After setting the Authentication in the context, we specify
59+
// that the current user is authenticated. So it passes the
60+
// Spring Security Configurations successfully.
61+
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
62+
}
63+
}
64+
chain.doFilter(request, response);
65+
}
66+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.javachinna.config;
2+
3+
import org.springframework.context.annotation.Configuration;
4+
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
5+
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
6+
7+
@Configuration
8+
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
9+
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
10+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /