update to spring 2.6.1

This commit is contained in:
Tien Nguyen Truong 2021-12-11 12:13:08 +07:00
parent 3c425d7a6f
commit e037bf6824
21 changed files with 521 additions and 479 deletions

View File

@ -31,11 +31,19 @@ For instruction: [Spring Boot Refresh Token with JWT example](https://bezkoder.c
> [Spring Boot + Angular 11 JWT Authentication](https://bezkoder.com/angular-11-spring-boot-jwt-auth/) > [Spring Boot + Angular 11 JWT Authentication](https://bezkoder.com/angular-11-spring-boot-jwt-auth/)
> [Spring Boot + Angular 12 JWT Authentication](https://www.bezkoder.com/angular-12-spring-boot-jwt-auth/)
> [Spring Boot + React JWT Authentication](https://bezkoder.com/spring-boot-react-jwt-auth/) > [Spring Boot + React JWT Authentication](https://bezkoder.com/spring-boot-react-jwt-auth/)
## Fullstack CRUD App ## Fullstack CRUD App
> [Vue.js + Spring Boot + MySQL/PostgreSQL example](https://bezkoder.com/spring-boot-vue-js-crud-example/) > [Vue.js + Spring Boot + H2 Embedded database example](https://www.bezkoder.com/spring-boot-vue-js-crud-example/)
> [Vue.js + Spring Boot + MySQL example](https://www.bezkoder.com/spring-boot-vue-js-mysql/)
> [Vue.js + Spring Boot + PostgreSQL example](https://www.bezkoder.com/spring-boot-vue-js-postgresql/)
> [Angular 8 + Spring Boot + Embedded database example](https://www.bezkoder.com/angular-spring-boot-crud/)
> [Angular 8 + Spring Boot + MySQL example](https://bezkoder.com/angular-spring-boot-crud/) > [Angular 8 + Spring Boot + MySQL example](https://bezkoder.com/angular-spring-boot-crud/)
@ -49,6 +57,18 @@ For instruction: [Spring Boot Refresh Token with JWT example](https://bezkoder.c
> [Angular 11 + Spring Boot + PostgreSQL example](https://bezkoder.com/angular-11-spring-boot-postgresql/) > [Angular 11 + Spring Boot + PostgreSQL example](https://bezkoder.com/angular-11-spring-boot-postgresql/)
> [Angular 12 + Spring Boot + Embedded database example](https://www.bezkoder.com/angular-12-spring-boot-crud/)
> [Angular 12 + Spring Boot + MySQL example](https://www.bezkoder.com/angular-12-spring-boot-mysql/)
> [Angular 12 + Spring Boot + PostgreSQL example](https://www.bezkoder.com/angular-12-spring-boot-postgresql/)
> [Angular 13 + Spring Boot + H2 Embedded Database example](https://www.bezkoder.com/spring-boot-angular-13-crud/)
> [Angular 13 + Spring Boot + MySQL example](https://www.bezkoder.com/spring-boot-angular-13-mysql/)
> [Angular 13 + Spring Boot + PostgreSQL example](https://www.bezkoder.com/spring-boot-angular-13-postgresql/)
> [React + Spring Boot + MySQL example](https://bezkoder.com/react-spring-boot-crud/) > [React + Spring Boot + MySQL example](https://bezkoder.com/react-spring-boot-crud/)
> [React + Spring Boot + PostgreSQL example](https://bezkoder.com/spring-boot-react-postgresql/) > [React + Spring Boot + PostgreSQL example](https://bezkoder.com/spring-boot-react-postgresql/)
@ -69,9 +89,10 @@ More Practice:
> [Spring Boot Repository Unit Test with @DataJpaTest](https://bezkoder.com/spring-boot-unit-test-jpa-repo-datajpatest/) > [Spring Boot Repository Unit Test with @DataJpaTest](https://bezkoder.com/spring-boot-unit-test-jpa-repo-datajpatest/)
> [Deploy Spring Boot App on AWS Elastic Beanstalk](https://bezkoder.com/deploy-spring-boot-aws-eb/) Deployment:
> [Deploy Spring Boot App on AWS Elastic Beanstalk](https://www.bezkoder.com/deploy-spring-boot-aws-eb/)
> [Secure Spring Boot App with Spring Security & JWT Authentication](https://bezkoder.com/spring-boot-jwt-authentication/) > [Docker Compose Spring Boot and MySQL example](https://www.bezkoder.com/docker-compose-spring-boot-mysql/)
## Dependency ## Dependency
If you want to use PostgreSQL: If you want to use PostgreSQL:

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version> <version>2.6.1</version>
<relativePath /> <!-- lookup parent from repository --> <relativePath /> <!-- lookup parent from repository -->
</parent> </parent>
<groupId>com.bezkoder</groupId> <groupId>com.bezkoder</groupId>
@ -29,6 +29,11 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId> <artifactId>spring-boot-starter-security</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@ -7,7 +7,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
public class SpringBootSecurityJwtApplication { public class SpringBootSecurityJwtApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(SpringBootSecurityJwtApplication.class, args); SpringApplication.run(SpringBootSecurityJwtApplication.class, args);
} }
} }

View File

@ -36,94 +36,94 @@ import com.bezkoder.springjwt.security.services.UserDetailsImpl;
@RestController @RestController
@RequestMapping("/api/auth") @RequestMapping("/api/auth")
public class AuthController { public class AuthController {
@Autowired @Autowired
AuthenticationManager authenticationManager; AuthenticationManager authenticationManager;
@Autowired @Autowired
UserRepository userRepository; UserRepository userRepository;
@Autowired @Autowired
RoleRepository roleRepository; RoleRepository roleRepository;
@Autowired @Autowired
PasswordEncoder encoder; PasswordEncoder encoder;
@Autowired @Autowired
JwtUtils jwtUtils; JwtUtils jwtUtils;
@PostMapping("/signin") @PostMapping("/signin")
public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) { public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate( Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())); new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication); SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtUtils.generateJwtToken(authentication); String jwt = jwtUtils.generateJwtToken(authentication);
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal(); UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
List<String> roles = userDetails.getAuthorities().stream() List<String> roles = userDetails.getAuthorities().stream()
.map(item -> item.getAuthority()) .map(item -> item.getAuthority())
.collect(Collectors.toList()); .collect(Collectors.toList());
return ResponseEntity.ok(new JwtResponse(jwt, return ResponseEntity.ok(new JwtResponse(jwt,
userDetails.getId(), userDetails.getId(),
userDetails.getUsername(), userDetails.getUsername(),
userDetails.getEmail(), userDetails.getEmail(),
roles)); roles));
} }
@PostMapping("/signup") @PostMapping("/signup")
public ResponseEntity<?> registerUser(@Valid @RequestBody SignupRequest signUpRequest) { public ResponseEntity<?> registerUser(@Valid @RequestBody SignupRequest signUpRequest) {
if (userRepository.existsByUsername(signUpRequest.getUsername())) { if (userRepository.existsByUsername(signUpRequest.getUsername())) {
return ResponseEntity return ResponseEntity
.badRequest() .badRequest()
.body(new MessageResponse("Error: Username is already taken!")); .body(new MessageResponse("Error: Username is already taken!"));
} }
if (userRepository.existsByEmail(signUpRequest.getEmail())) { if (userRepository.existsByEmail(signUpRequest.getEmail())) {
return ResponseEntity return ResponseEntity
.badRequest() .badRequest()
.body(new MessageResponse("Error: Email is already in use!")); .body(new MessageResponse("Error: Email is already in use!"));
} }
// Create new user's account // Create new user's account
User user = new User(signUpRequest.getUsername(), User user = new User(signUpRequest.getUsername(),
signUpRequest.getEmail(), signUpRequest.getEmail(),
encoder.encode(signUpRequest.getPassword())); encoder.encode(signUpRequest.getPassword()));
Set<String> strRoles = signUpRequest.getRole(); Set<String> strRoles = signUpRequest.getRole();
Set<Role> roles = new HashSet<>(); Set<Role> roles = new HashSet<>();
if (strRoles == null) { if (strRoles == null) {
Role userRole = roleRepository.findByName(ERole.ROLE_USER) Role userRole = roleRepository.findByName(ERole.ROLE_USER)
.orElseThrow(() -> new RuntimeException("Error: Role is not found.")); .orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(userRole); roles.add(userRole);
} else { } else {
strRoles.forEach(role -> { strRoles.forEach(role -> {
switch (role) { switch (role) {
case "admin": case "admin":
Role adminRole = roleRepository.findByName(ERole.ROLE_ADMIN) Role adminRole = roleRepository.findByName(ERole.ROLE_ADMIN)
.orElseThrow(() -> new RuntimeException("Error: Role is not found.")); .orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(adminRole); roles.add(adminRole);
break; break;
case "mod": case "mod":
Role modRole = roleRepository.findByName(ERole.ROLE_MODERATOR) Role modRole = roleRepository.findByName(ERole.ROLE_MODERATOR)
.orElseThrow(() -> new RuntimeException("Error: Role is not found.")); .orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(modRole); roles.add(modRole);
break; break;
default: default:
Role userRole = roleRepository.findByName(ERole.ROLE_USER) Role userRole = roleRepository.findByName(ERole.ROLE_USER)
.orElseThrow(() -> new RuntimeException("Error: Role is not found.")); .orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(userRole); roles.add(userRole);
} }
}); });
} }
user.setRoles(roles); user.setRoles(roles);
userRepository.save(user); userRepository.save(user);
return ResponseEntity.ok(new MessageResponse("User registered successfully!")); return ResponseEntity.ok(new MessageResponse("User registered successfully!"));
} }
} }

View File

@ -10,26 +10,26 @@ import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
@RequestMapping("/api/test") @RequestMapping("/api/test")
public class TestController { public class TestController {
@GetMapping("/all") @GetMapping("/all")
public String allAccess() { public String allAccess() {
return "Public Content."; return "Public Content.";
} }
@GetMapping("/user")
@PreAuthorize("hasRole('USER') or hasRole('MODERATOR') or hasRole('ADMIN')")
public String userAccess() {
return "User Content.";
}
@GetMapping("/mod") @GetMapping("/user")
@PreAuthorize("hasRole('MODERATOR')") @PreAuthorize("hasRole('USER') or hasRole('MODERATOR') or hasRole('ADMIN')")
public String moderatorAccess() { public String userAccess() {
return "Moderator Board."; return "User Content.";
} }
@GetMapping("/admin") @GetMapping("/mod")
@PreAuthorize("hasRole('ADMIN')") @PreAuthorize("hasRole('MODERATOR')")
public String adminAccess() { public String moderatorAccess() {
return "Admin Board."; return "Moderator Board.";
} }
@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')")
public String adminAccess() {
return "Admin Board.";
}
} }

View File

@ -1,7 +1,7 @@
package com.bezkoder.springjwt.models; package com.bezkoder.springjwt.models;
public enum ERole { public enum ERole {
ROLE_USER, ROLE_USER,
ROLE_MODERATOR, ROLE_MODERATOR,
ROLE_ADMIN ROLE_ADMIN
} }

View File

@ -5,35 +5,35 @@ import javax.persistence.*;
@Entity @Entity
@Table(name = "roles") @Table(name = "roles")
public class Role { public class Role {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id; private Integer id;
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
@Column(length = 20) @Column(length = 20)
private ERole name; private ERole name;
public Role() { public Role() {
} }
public Role(ERole name) { public Role(ERole name) {
this.name = name; this.name = name;
} }
public Integer getId() { public Integer getId() {
return id; return id;
} }
public void setId(Integer id) { public void setId(Integer id) {
this.id = id; this.id = id;
} }
public ERole getName() { public ERole getName() {
return name; return name;
} }
public void setName(ERole name) { public void setName(ERole name) {
this.name = name; this.name = name;
} }
} }

View File

@ -9,81 +9,81 @@ import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
@Entity @Entity
@Table( name = "users", @Table(name = "users",
uniqueConstraints = { uniqueConstraints = {
@UniqueConstraint(columnNames = "username"), @UniqueConstraint(columnNames = "username"),
@UniqueConstraint(columnNames = "email") @UniqueConstraint(columnNames = "email")
}) })
public class User { public class User {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; private Long id;
@NotBlank @NotBlank
@Size(max = 20) @Size(max = 20)
private String username; private String username;
@NotBlank @NotBlank
@Size(max = 50) @Size(max = 50)
@Email @Email
private String email; private String email;
@NotBlank @NotBlank
@Size(max = 120) @Size(max = 120)
private String password; private String password;
@ManyToMany(fetch = FetchType.LAZY) @ManyToMany(fetch = FetchType.LAZY)
@JoinTable( name = "user_roles", @JoinTable( name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"), joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")) inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>(); private Set<Role> roles = new HashSet<>();
public User() { public User() {
} }
public User(String username, String email, String password) { public User(String username, String email, String password) {
this.username = username; this.username = username;
this.email = email; this.email = email;
this.password = password; this.password = password;
} }
public Long getId() { public Long getId() {
return id; return id;
} }
public void setId(Long id) { public void setId(Long id) {
this.id = id; this.id = id;
} }
public String getUsername() { public String getUsername() {
return username; return username;
} }
public void setUsername(String username) { public void setUsername(String username) {
this.username = username; this.username = username;
} }
public String getEmail() { public String getEmail() {
return email; return email;
} }
public void setEmail(String email) { public void setEmail(String email) {
this.email = email; this.email = email;
} }
public String getPassword() { public String getPassword() {
return password; return password;
} }
public void setPassword(String password) { public void setPassword(String password) {
this.password = password; this.password = password;
} }
public Set<Role> getRoles() { public Set<Role> getRoles() {
return roles; return roles;
} }
public void setRoles(Set<Role> roles) { public void setRoles(Set<Role> roles) {
this.roles = roles; this.roles = roles;
} }
} }

View File

@ -4,7 +4,7 @@ import javax.validation.constraints.NotBlank;
public class LoginRequest { public class LoginRequest {
@NotBlank @NotBlank
private String username; private String username;
@NotBlank @NotBlank
private String password; private String password;

View File

@ -3,52 +3,52 @@ package com.bezkoder.springjwt.payload.request;
import java.util.Set; import java.util.Set;
import javax.validation.constraints.*; import javax.validation.constraints.*;
public class SignupRequest { public class SignupRequest {
@NotBlank @NotBlank
@Size(min = 3, max = 20) @Size(min = 3, max = 20)
private String username; private String username;
@NotBlank @NotBlank
@Size(max = 50) @Size(max = 50)
@Email @Email
private String email; private String email;
private Set<String> role; private Set<String> role;
@NotBlank @NotBlank
@Size(min = 6, max = 40) @Size(min = 6, max = 40)
private String password; private String password;
public String getUsername() { public String getUsername() {
return username; return username;
} }
public void setUsername(String username) { public void setUsername(String username) {
this.username = username; this.username = username;
} }
public String getEmail() { public String getEmail() {
return email; return email;
} }
public void setEmail(String email) { public void setEmail(String email) {
this.email = email; this.email = email;
} }
public String getPassword() { public String getPassword() {
return password; return password;
} }
public void setPassword(String password) { public void setPassword(String password) {
this.password = password; this.password = password;
} }
public Set<String> getRole() { public Set<String> getRole() {
return this.role; return this.role;
} }
public void setRole(Set<String> role) { public void setRole(Set<String> role) {
this.role = role; this.role = role;
} }
} }

View File

@ -3,62 +3,62 @@ package com.bezkoder.springjwt.payload.response;
import java.util.List; import java.util.List;
public class JwtResponse { public class JwtResponse {
private String token; private String token;
private String type = "Bearer"; private String type = "Bearer";
private Long id; private Long id;
private String username; private String username;
private String email; private String email;
private List<String> roles; private List<String> roles;
public JwtResponse(String accessToken, Long id, String username, String email, List<String> roles) { public JwtResponse(String accessToken, Long id, String username, String email, List<String> roles) {
this.token = accessToken; this.token = accessToken;
this.id = id; this.id = id;
this.username = username; this.username = username;
this.email = email; this.email = email;
this.roles = roles; this.roles = roles;
} }
public String getAccessToken() { public String getAccessToken() {
return token; return token;
} }
public void setAccessToken(String accessToken) { public void setAccessToken(String accessToken) {
this.token = accessToken; this.token = accessToken;
} }
public String getTokenType() { public String getTokenType() {
return type; return type;
} }
public void setTokenType(String tokenType) { public void setTokenType(String tokenType) {
this.type = tokenType; this.type = tokenType;
} }
public Long getId() { public Long getId() {
return id; return id;
} }
public void setId(Long id) { public void setId(Long id) {
this.id = id; this.id = id;
} }
public String getEmail() { public String getEmail() {
return email; return email;
} }
public void setEmail(String email) { public void setEmail(String email) {
this.email = email; this.email = email;
} }
public String getUsername() { public String getUsername() {
return username; return username;
} }
public void setUsername(String username) { public void setUsername(String username) {
this.username = username; this.username = username;
} }
public List<String> getRoles() { public List<String> getRoles() {
return roles; return roles;
} }
} }

View File

@ -1,17 +1,17 @@
package com.bezkoder.springjwt.payload.response; package com.bezkoder.springjwt.payload.response;
public class MessageResponse { public class MessageResponse {
private String message; private String message;
public MessageResponse(String message) { public MessageResponse(String message) {
this.message = message; this.message = message;
} }
public String getMessage() { public String getMessage() {
return message; return message;
} }
public void setMessage(String message) { public void setMessage(String message) {
this.message = message; this.message = message;
} }
} }

View File

@ -10,5 +10,5 @@ import com.bezkoder.springjwt.models.Role;
@Repository @Repository
public interface RoleRepository extends JpaRepository<Role, Long> { public interface RoleRepository extends JpaRepository<Role, Long> {
Optional<Role> findByName(ERole name); Optional<Role> findByName(ERole name);
} }

View File

@ -9,9 +9,9 @@ import com.bezkoder.springjwt.models.User;
@Repository @Repository
public interface UserRepository extends JpaRepository<User, Long> { public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username); Optional<User> findByUsername(String username);
Boolean existsByUsername(String username); Boolean existsByUsername(String username);
Boolean existsByEmail(String email); Boolean existsByEmail(String email);
} }

View File

@ -21,46 +21,46 @@ import com.bezkoder.springjwt.security.services.UserDetailsServiceImpl;
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@EnableGlobalMethodSecurity( @EnableGlobalMethodSecurity(
// securedEnabled = true, // securedEnabled = true,
// jsr250Enabled = true, // jsr250Enabled = true,
prePostEnabled = true) prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired @Autowired
UserDetailsServiceImpl userDetailsService; UserDetailsServiceImpl userDetailsService;
@Autowired @Autowired
private AuthEntryPointJwt unauthorizedHandler; private AuthEntryPointJwt unauthorizedHandler;
@Bean @Bean
public AuthTokenFilter authenticationJwtTokenFilter() { public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter(); return new AuthTokenFilter();
} }
@Override @Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
} }
@Bean @Bean
@Override @Override
public AuthenticationManager authenticationManagerBean() throws Exception { public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean(); return super.authenticationManagerBean();
} }
@Bean @Bean
public PasswordEncoder passwordEncoder() { public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); return new BCryptPasswordEncoder();
} }
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable() http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers("/api/auth/**").permitAll() .authorizeRequests().antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/test/**").permitAll() .antMatchers("/api/test/**").permitAll()
.anyRequest().authenticated(); .anyRequest().authenticated();
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
} }
} }

View File

@ -1,6 +1,8 @@
package com.bezkoder.springjwt.security.jwt; package com.bezkoder.springjwt.security.jwt;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -8,20 +10,34 @@ import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;
@Component @Component
public class AuthEntryPointJwt implements AuthenticationEntryPoint { public class AuthEntryPointJwt implements AuthenticationEntryPoint {
private static final Logger logger = LoggerFactory.getLogger(AuthEntryPointJwt.class); private static final Logger logger = LoggerFactory.getLogger(AuthEntryPointJwt.class);
@Override @Override
public void commence(HttpServletRequest request, HttpServletResponse response, public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
AuthenticationException authException) throws IOException, ServletException { throws IOException, ServletException {
logger.error("Unauthorized error: {}", authException.getMessage()); logger.error("Unauthorized error: {}", authException.getMessage());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized");
} response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
final Map<String, Object> body = new HashMap<>();
body.put("status", HttpServletResponse.SC_UNAUTHORIZED);
body.put("error", "Unauthorized");
body.put("message", authException.getMessage());
body.put("path", request.getServletPath());
final ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.getOutputStream(), body);
}
} }

View File

@ -20,43 +20,46 @@ import org.springframework.web.filter.OncePerRequestFilter;
import com.bezkoder.springjwt.security.services.UserDetailsServiceImpl; import com.bezkoder.springjwt.security.services.UserDetailsServiceImpl;
public class AuthTokenFilter extends OncePerRequestFilter { public class AuthTokenFilter extends OncePerRequestFilter {
@Autowired @Autowired
private JwtUtils jwtUtils; private JwtUtils jwtUtils;
@Autowired @Autowired
private UserDetailsServiceImpl userDetailsService; private UserDetailsServiceImpl userDetailsService;
private static final Logger logger = LoggerFactory.getLogger(AuthTokenFilter.class); private static final Logger logger = LoggerFactory.getLogger(AuthTokenFilter.class);
@Override @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException { throws ServletException, IOException {
try { try {
String jwt = parseJwt(request); String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateJwtToken(jwt)) { if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
String username = jwtUtils.getUserNameFromJwtToken(jwt); String username = jwtUtils.getUserNameFromJwtToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username); UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( UsernamePasswordAuthenticationToken authentication =
userDetails, null, userDetails.getAuthorities()); new UsernamePasswordAuthenticationToken(
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); userDetails,
null,
userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication); SecurityContextHolder.getContext().setAuthentication(authentication);
} }
} catch (Exception e) { } catch (Exception e) {
logger.error("Cannot set user authentication: {}", e); logger.error("Cannot set user authentication: {}", e);
} }
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
} }
private String parseJwt(HttpServletRequest request) { private String parseJwt(HttpServletRequest request) {
String headerAuth = request.getHeader("Authorization"); String headerAuth = request.getHeader("Authorization");
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) { if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
return headerAuth.substring(7, headerAuth.length()); return headerAuth.substring(7, headerAuth.length());
} }
return null; return null;
} }
} }

View File

@ -13,46 +13,46 @@ import io.jsonwebtoken.*;
@Component @Component
public class JwtUtils { public class JwtUtils {
private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class); private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
@Value("${bezkoder.app.jwtSecret}") @Value("${bezkoder.app.jwtSecret}")
private String jwtSecret; private String jwtSecret;
@Value("${bezkoder.app.jwtExpirationMs}") @Value("${bezkoder.app.jwtExpirationMs}")
private int jwtExpirationMs; private int jwtExpirationMs;
public String generateJwtToken(Authentication authentication) { public String generateJwtToken(Authentication authentication) {
UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal(); UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();
return Jwts.builder() return Jwts.builder()
.setSubject((userPrincipal.getUsername())) .setSubject((userPrincipal.getUsername()))
.setIssuedAt(new Date()) .setIssuedAt(new Date())
.setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)) .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs))
.signWith(SignatureAlgorithm.HS512, jwtSecret) .signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact(); .compact();
} }
public String getUserNameFromJwtToken(String token) { public String getUserNameFromJwtToken(String token) {
return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject(); return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject();
} }
public boolean validateJwtToken(String authToken) { public boolean validateJwtToken(String authToken) {
try { try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken); Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true; return true;
} catch (SignatureException e) { } catch (SignatureException e) {
logger.error("Invalid JWT signature: {}", e.getMessage()); logger.error("Invalid JWT signature: {}", e.getMessage());
} catch (MalformedJwtException e) { } catch (MalformedJwtException e) {
logger.error("Invalid JWT token: {}", e.getMessage()); logger.error("Invalid JWT token: {}", e.getMessage());
} catch (ExpiredJwtException e) { } catch (ExpiredJwtException e) {
logger.error("JWT token is expired: {}", e.getMessage()); logger.error("JWT token is expired: {}", e.getMessage());
} catch (UnsupportedJwtException e) { } catch (UnsupportedJwtException e) {
logger.error("JWT token is unsupported: {}", e.getMessage()); logger.error("JWT token is unsupported: {}", e.getMessage());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
logger.error("JWT claims string is empty: {}", e.getMessage()); logger.error("JWT claims string is empty: {}", e.getMessage());
} }
return false; return false;
} }
} }

View File

@ -13,91 +13,91 @@ import com.bezkoder.springjwt.models.User;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
public class UserDetailsImpl implements UserDetails { public class UserDetailsImpl implements UserDetails {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private Long id; private Long id;
private String username; private String username;
private String email; private String email;
@JsonIgnore @JsonIgnore
private String password; private String password;
private Collection<? extends GrantedAuthority> authorities; private Collection<? extends GrantedAuthority> authorities;
public UserDetailsImpl(Long id, String username, String email, String password, public UserDetailsImpl(Long id, String username, String email, String password,
Collection<? extends GrantedAuthority> authorities) { Collection<? extends GrantedAuthority> authorities) {
this.id = id; this.id = id;
this.username = username; this.username = username;
this.email = email; this.email = email;
this.password = password; this.password = password;
this.authorities = authorities; this.authorities = authorities;
} }
public static UserDetailsImpl build(User user) { public static UserDetailsImpl build(User user) {
List<GrantedAuthority> authorities = user.getRoles().stream() List<GrantedAuthority> authorities = user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority(role.getName().name())) .map(role -> new SimpleGrantedAuthority(role.getName().name()))
.collect(Collectors.toList()); .collect(Collectors.toList());
return new UserDetailsImpl( return new UserDetailsImpl(
user.getId(), user.getId(),
user.getUsername(), user.getUsername(),
user.getEmail(), user.getEmail(),
user.getPassword(), user.getPassword(),
authorities); authorities);
} }
@Override @Override
public Collection<? extends GrantedAuthority> getAuthorities() { public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities; return authorities;
} }
public Long getId() { public Long getId() {
return id; return id;
} }
public String getEmail() { public String getEmail() {
return email; return email;
} }
@Override @Override
public String getPassword() { public String getPassword() {
return password; return password;
} }
@Override @Override
public String getUsername() { public String getUsername() {
return username; return username;
} }
@Override @Override
public boolean isAccountNonExpired() { public boolean isAccountNonExpired() {
return true; return true;
} }
@Override @Override
public boolean isAccountNonLocked() { public boolean isAccountNonLocked() {
return true; return true;
} }
@Override @Override
public boolean isCredentialsNonExpired() { public boolean isCredentialsNonExpired() {
return true; return true;
} }
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
return true; return true;
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) if (this == o)
return true; return true;
if (o == null || getClass() != o.getClass()) if (o == null || getClass() != o.getClass())
return false; return false;
UserDetailsImpl user = (UserDetailsImpl) o; UserDetailsImpl user = (UserDetailsImpl) o;
return Objects.equals(id, user.id); return Objects.equals(id, user.id);
} }
} }

View File

@ -12,16 +12,16 @@ import com.bezkoder.springjwt.repository.UserRepository;
@Service @Service
public class UserDetailsServiceImpl implements UserDetailsService { public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired @Autowired
UserRepository userRepository; UserRepository userRepository;
@Override @Override
@Transactional @Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username) User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username)); .orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username));
return UserDetailsImpl.build(user); return UserDetailsImpl.build(user);
} }
} }

View File

@ -1,11 +1,8 @@
package com.bezkoder.springjwt; package com.bezkoder.springjwt;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest
public class SpringBootSecurityJwtApplicationTests { public class SpringBootSecurityJwtApplicationTests {