From 39a9a4a4ececeee780be596c70dfc65417409d43 Mon Sep 17 00:00:00 2001 From: Cekis Date: Sun, 9 May 2021 07:16:39 -0700 Subject: [PATCH] Added Authentication with Login --- pom.xml | 9 ++ .../java/swg/controller/AuthController.java | 59 +++++++++ .../controller/ResourceTypeController.java | 4 +- src/main/java/swg/entity/UasUser.java | 84 ++++++++++++ src/main/java/swg/entity/UserRole.java | 7 + .../swg/payload/request/LoginRequest.java | 27 ++++ .../swg/payload/response/JwtResponse.java | 64 +++++++++ .../swg/payload/response/MessageResponse.java | 17 +++ .../swg/{dao => repository}/AccountDao.java | 2 +- .../{dao => repository}/CityObjectDao.java | 5 +- .../swg/{dao => repository}/ClockDao.java | 2 +- .../{dao => repository}/MarketAuctionDao.java | 2 +- .../swg/{dao => repository}/ObjectsDao.java | 2 +- .../swg/{dao => repository}/PlayerDao.java | 2 +- .../{dao => repository}/PlayerObjectDao.java | 2 +- .../{dao => repository}/PropertyListDao.java | 4 +- .../{dao => repository}/ResourceTypeDao.java | 2 +- src/main/java/swg/repository/UasUserDao.java | 14 ++ .../java/swg/security/WebSecurityConfig.java | 66 ++++++++++ .../swg/security/jwt/AuthEntryPointJwt.java | 23 ++++ .../swg/security/jwt/AuthTokenFilter.java | 62 +++++++++ src/main/java/swg/security/jwt/JwtUtils.java | 55 ++++++++ src/main/java/swg/service/AccountService.java | 2 +- .../java/swg/service/CityObjectService.java | 4 +- src/main/java/swg/service/ClockService.java | 2 +- .../swg/service/MarketAuctionService.java | 2 +- src/main/java/swg/service/PlayerService.java | 8 +- .../java/swg/service/PropertyListService.java | 4 +- .../java/swg/service/ResourceTypeService.java | 2 +- .../swg/service/UasUserDetailsService.java | 8 ++ .../java/swg/service/UserDetailsImpl.java | 122 ++++++++++++++++++ .../swg/service/UserDetailsServiceImpl.java | 27 ++++ src/main/resources/application.properties | 3 + 33 files changed, 669 insertions(+), 29 deletions(-) create mode 100644 src/main/java/swg/controller/AuthController.java create mode 100644 src/main/java/swg/entity/UasUser.java create mode 100644 src/main/java/swg/entity/UserRole.java create mode 100644 src/main/java/swg/payload/request/LoginRequest.java create mode 100644 src/main/java/swg/payload/response/JwtResponse.java create mode 100644 src/main/java/swg/payload/response/MessageResponse.java rename src/main/java/swg/{dao => repository}/AccountDao.java (92%) rename src/main/java/swg/{dao => repository}/CityObjectDao.java (88%) rename src/main/java/swg/{dao => repository}/ClockDao.java (90%) rename src/main/java/swg/{dao => repository}/MarketAuctionDao.java (92%) rename src/main/java/swg/{dao => repository}/ObjectsDao.java (96%) rename src/main/java/swg/{dao => repository}/PlayerDao.java (93%) rename src/main/java/swg/{dao => repository}/PlayerObjectDao.java (92%) rename src/main/java/swg/{dao => repository}/PropertyListDao.java (81%) rename src/main/java/swg/{dao => repository}/ResourceTypeDao.java (95%) create mode 100644 src/main/java/swg/repository/UasUserDao.java create mode 100644 src/main/java/swg/security/WebSecurityConfig.java create mode 100644 src/main/java/swg/security/jwt/AuthEntryPointJwt.java create mode 100644 src/main/java/swg/security/jwt/AuthTokenFilter.java create mode 100644 src/main/java/swg/security/jwt/JwtUtils.java create mode 100644 src/main/java/swg/service/UasUserDetailsService.java create mode 100644 src/main/java/swg/service/UserDetailsImpl.java create mode 100644 src/main/java/swg/service/UserDetailsServiceImpl.java diff --git a/pom.xml b/pom.xml index bc6325e..b0dccd4 100644 --- a/pom.xml +++ b/pom.xml @@ -57,6 +57,15 @@ hibernate-validator 6.0.16.Final + + io.jsonwebtoken + jjwt + 0.9.1 + + + org.springframework.boot + spring-boot-starter-security + diff --git a/src/main/java/swg/controller/AuthController.java b/src/main/java/swg/controller/AuthController.java new file mode 100644 index 0000000..3a4166e --- /dev/null +++ b/src/main/java/swg/controller/AuthController.java @@ -0,0 +1,59 @@ +package swg.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.web.bind.annotation.*; +import swg.payload.request.LoginRequest; +import swg.payload.response.JwtResponse; +import swg.repository.UasUserDao; +import swg.security.jwt.JwtUtils; +import swg.service.UserDetailsImpl; + +import javax.validation.Valid; +import java.util.List; +import java.util.stream.Collectors; + +@CrossOrigin(origins = "*", maxAge = 3600) +@RestController +@RequestMapping("/api/auth") +public class AuthController { + @Autowired + AuthenticationManager authenticationManager; + + @Autowired + UasUserDao userRepository; + + @Autowired + PasswordEncoder encoder; + + @Autowired + JwtUtils jwtUtils; + + @PostMapping("/login") + public ResponseEntity authenticateUser(@Valid @RequestBody LoginRequest loginRequest) { + UsernamePasswordAuthenticationToken authReq = new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()); + + Authentication authentication = authenticationManager.authenticate(authReq); + + SecurityContextHolder.getContext().setAuthentication(authentication); + String jwt = jwtUtils.generateJwtToken(authentication); + + UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal(); + List roles = userDetails.getAuthorities().stream() + .map(item-> item.getAuthority()) + .collect(Collectors.toList()); + + return ResponseEntity.ok(new JwtResponse( + jwt, + userDetails.getId(), + userDetails.getUsername(), + userDetails.getEmail(), + roles + )); + } +} diff --git a/src/main/java/swg/controller/ResourceTypeController.java b/src/main/java/swg/controller/ResourceTypeController.java index c3ce1d7..5b7ae0d 100644 --- a/src/main/java/swg/controller/ResourceTypeController.java +++ b/src/main/java/swg/controller/ResourceTypeController.java @@ -1,6 +1,7 @@ package swg.controller; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -11,7 +12,7 @@ import swg.service.ResourceTypeService; import java.util.List; -@CrossOrigin +@CrossOrigin(origins = "*", maxAge = 3600) @RestController @RequestMapping("/api/resource") public class ResourceTypeController { @@ -20,6 +21,7 @@ public class ResourceTypeController { ClockService clockService; @RequestMapping(value = "/historical/all", method = RequestMethod.GET) + @PreAuthorize("hasRole('ROLE_ADMIN')") public List getAllResources() { return resourceTypeService.getAllResources(); } diff --git a/src/main/java/swg/entity/UasUser.java b/src/main/java/swg/entity/UasUser.java new file mode 100644 index 0000000..6389e80 --- /dev/null +++ b/src/main/java/swg/entity/UasUser.java @@ -0,0 +1,84 @@ +package swg.entity; + +import javax.persistence.*; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@Entity +@Table(name = "UAS_USERS", + uniqueConstraints = { + @UniqueConstraint(columnNames = "USERNAME"), + @UniqueConstraint(columnNames = "EMAIL") + }) +public class UasUser { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotBlank + @Size(max = 20) + private String username; + + @NotBlank + @Size(max = 50) + @Email + private String email; + + @NotBlank + @Size(max = 120) + private String password; + + @NotBlank + @Enumerated(EnumType.STRING) + private UserRole userrole; + + public UasUser() { + } + + public UasUser(String username, String email, String password) { + this.username = username; + this.email = email; + this.password = password; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public UserRole getUserRole() { + return userrole; + } + + public void setUserRole(UserRole userRole) { + this.userrole = userRole; + } +} diff --git a/src/main/java/swg/entity/UserRole.java b/src/main/java/swg/entity/UserRole.java new file mode 100644 index 0000000..33014c9 --- /dev/null +++ b/src/main/java/swg/entity/UserRole.java @@ -0,0 +1,7 @@ +package swg.entity; + +public enum UserRole { + ROLE_USER, + ROLE_MODERATOR, + ROLE_ADMIN +} diff --git a/src/main/java/swg/payload/request/LoginRequest.java b/src/main/java/swg/payload/request/LoginRequest.java new file mode 100644 index 0000000..dd41715 --- /dev/null +++ b/src/main/java/swg/payload/request/LoginRequest.java @@ -0,0 +1,27 @@ +package swg.payload.request; + +import javax.validation.constraints.NotBlank; + +public class LoginRequest { + @NotBlank + private String username; + + @NotBlank + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} \ No newline at end of file diff --git a/src/main/java/swg/payload/response/JwtResponse.java b/src/main/java/swg/payload/response/JwtResponse.java new file mode 100644 index 0000000..c4ccbfc --- /dev/null +++ b/src/main/java/swg/payload/response/JwtResponse.java @@ -0,0 +1,64 @@ +package swg.payload.response; + +import java.util.List; + +public class JwtResponse { + private String token; + private String type = "Bearer"; + private Long id; + private String username; + private String email; + private List roles; + + public JwtResponse(String accessToken, Long id, String username, String email, List roles) { + this.token = accessToken; + this.id = id; + this.username = username; + this.email = email; + this.roles = roles; + } + + public String getAccessToken() { + return token; + } + + public void setAccessToken(String accessToken) { + this.token = accessToken; + } + + public String getTokenType() { + return type; + } + + public void setTokenType(String tokenType) { + this.type = tokenType; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public List getRoles() { + return roles; + } +} \ No newline at end of file diff --git a/src/main/java/swg/payload/response/MessageResponse.java b/src/main/java/swg/payload/response/MessageResponse.java new file mode 100644 index 0000000..b51e311 --- /dev/null +++ b/src/main/java/swg/payload/response/MessageResponse.java @@ -0,0 +1,17 @@ +package swg.payload.response; + +public class MessageResponse { + private String message; + + public MessageResponse(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} \ No newline at end of file diff --git a/src/main/java/swg/dao/AccountDao.java b/src/main/java/swg/repository/AccountDao.java similarity index 92% rename from src/main/java/swg/dao/AccountDao.java rename to src/main/java/swg/repository/AccountDao.java index b467919..fb9f68f 100644 --- a/src/main/java/swg/dao/AccountDao.java +++ b/src/main/java/swg/repository/AccountDao.java @@ -1,4 +1,4 @@ -package swg.dao; +package swg.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/java/swg/dao/CityObjectDao.java b/src/main/java/swg/repository/CityObjectDao.java similarity index 88% rename from src/main/java/swg/dao/CityObjectDao.java rename to src/main/java/swg/repository/CityObjectDao.java index 86053ba..668d56f 100644 --- a/src/main/java/swg/dao/CityObjectDao.java +++ b/src/main/java/swg/repository/CityObjectDao.java @@ -1,12 +1,9 @@ -package swg.dao; +package swg.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import swg.entity.CityObject; -import swg.entity.PropertyList; - -import java.util.List; @Repository public interface CityObjectDao extends JpaRepository { diff --git a/src/main/java/swg/dao/ClockDao.java b/src/main/java/swg/repository/ClockDao.java similarity index 90% rename from src/main/java/swg/dao/ClockDao.java rename to src/main/java/swg/repository/ClockDao.java index 562a0c7..d09e53e 100644 --- a/src/main/java/swg/dao/ClockDao.java +++ b/src/main/java/swg/repository/ClockDao.java @@ -1,4 +1,4 @@ -package swg.dao; +package swg.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/java/swg/dao/MarketAuctionDao.java b/src/main/java/swg/repository/MarketAuctionDao.java similarity index 92% rename from src/main/java/swg/dao/MarketAuctionDao.java rename to src/main/java/swg/repository/MarketAuctionDao.java index 7a88834..d94fd74 100644 --- a/src/main/java/swg/dao/MarketAuctionDao.java +++ b/src/main/java/swg/repository/MarketAuctionDao.java @@ -1,4 +1,4 @@ -package swg.dao; +package swg.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/java/swg/dao/ObjectsDao.java b/src/main/java/swg/repository/ObjectsDao.java similarity index 96% rename from src/main/java/swg/dao/ObjectsDao.java rename to src/main/java/swg/repository/ObjectsDao.java index 33fab41..132bcf5 100644 --- a/src/main/java/swg/dao/ObjectsDao.java +++ b/src/main/java/swg/repository/ObjectsDao.java @@ -1,4 +1,4 @@ -package swg.dao; +package swg.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; diff --git a/src/main/java/swg/dao/PlayerDao.java b/src/main/java/swg/repository/PlayerDao.java similarity index 93% rename from src/main/java/swg/dao/PlayerDao.java rename to src/main/java/swg/repository/PlayerDao.java index 8668f78..9b0c375 100644 --- a/src/main/java/swg/dao/PlayerDao.java +++ b/src/main/java/swg/repository/PlayerDao.java @@ -1,4 +1,4 @@ -package swg.dao; +package swg.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/java/swg/dao/PlayerObjectDao.java b/src/main/java/swg/repository/PlayerObjectDao.java similarity index 92% rename from src/main/java/swg/dao/PlayerObjectDao.java rename to src/main/java/swg/repository/PlayerObjectDao.java index 2390ec4..f53d5aa 100644 --- a/src/main/java/swg/dao/PlayerObjectDao.java +++ b/src/main/java/swg/repository/PlayerObjectDao.java @@ -1,4 +1,4 @@ -package swg.dao; +package swg.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/java/swg/dao/PropertyListDao.java b/src/main/java/swg/repository/PropertyListDao.java similarity index 81% rename from src/main/java/swg/dao/PropertyListDao.java rename to src/main/java/swg/repository/PropertyListDao.java index 8abc93f..a377959 100644 --- a/src/main/java/swg/dao/PropertyListDao.java +++ b/src/main/java/swg/repository/PropertyListDao.java @@ -1,10 +1,8 @@ -package swg.dao; +package swg.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import swg.entity.Player; import swg.entity.PropertyList; -import swg.entity.ResourceType; import java.util.List; diff --git a/src/main/java/swg/dao/ResourceTypeDao.java b/src/main/java/swg/repository/ResourceTypeDao.java similarity index 95% rename from src/main/java/swg/dao/ResourceTypeDao.java rename to src/main/java/swg/repository/ResourceTypeDao.java index a8220d2..8a87e92 100644 --- a/src/main/java/swg/dao/ResourceTypeDao.java +++ b/src/main/java/swg/repository/ResourceTypeDao.java @@ -1,4 +1,4 @@ -package swg.dao; +package swg.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; diff --git a/src/main/java/swg/repository/UasUserDao.java b/src/main/java/swg/repository/UasUserDao.java new file mode 100644 index 0000000..e8b4660 --- /dev/null +++ b/src/main/java/swg/repository/UasUserDao.java @@ -0,0 +1,14 @@ +package swg.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import swg.entity.UasUser; + +import java.util.Optional; + +@Repository +public interface UasUserDao extends JpaRepository { + Optional findByUsername(String username); + Boolean existsByUsername(String username); + Boolean existsByEmail(String email); +} \ No newline at end of file diff --git a/src/main/java/swg/security/WebSecurityConfig.java b/src/main/java/swg/security/WebSecurityConfig.java new file mode 100644 index 0000000..1a446e7 --- /dev/null +++ b/src/main/java/swg/security/WebSecurityConfig.java @@ -0,0 +1,66 @@ +package swg.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import swg.security.jwt.AuthEntryPointJwt; +import swg.security.jwt.AuthTokenFilter; +import swg.service.UserDetailsServiceImpl; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity( + // securedEnabled = true, + // jsr250Enabled = true, + prePostEnabled = true) +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + @Autowired + UserDetailsServiceImpl userDetailsService; + + @Autowired + private AuthEntryPointJwt unauthorizedHandler; + + @Bean + public AuthTokenFilter authenticationJwtTokenFilter() { + return new AuthTokenFilter(); + } + + @Override + public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { + authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); + } + + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.cors().and().csrf().disable() + .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() + .authorizeRequests() + .antMatchers("/api/auth/**").permitAll() +// .antMatchers("/api/test/**").permitAll() + .anyRequest().authenticated(); + + http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); + } +} \ No newline at end of file diff --git a/src/main/java/swg/security/jwt/AuthEntryPointJwt.java b/src/main/java/swg/security/jwt/AuthEntryPointJwt.java new file mode 100644 index 0000000..1bd3bbb --- /dev/null +++ b/src/main/java/swg/security/jwt/AuthEntryPointJwt.java @@ -0,0 +1,23 @@ +package swg.security.jwt; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +public class AuthEntryPointJwt implements AuthenticationEntryPoint { + private static final Logger logger = LoggerFactory.getLogger(AuthEntryPointJwt.class); + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + logger.error("Unauthorized error: {}", authException.getMessage()); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized"); + } +} diff --git a/src/main/java/swg/security/jwt/AuthTokenFilter.java b/src/main/java/swg/security/jwt/AuthTokenFilter.java new file mode 100644 index 0000000..2f3105d --- /dev/null +++ b/src/main/java/swg/security/jwt/AuthTokenFilter.java @@ -0,0 +1,62 @@ +package swg.security.jwt; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; +import swg.service.UserDetailsServiceImpl; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class AuthTokenFilter extends OncePerRequestFilter { + @Autowired + private JwtUtils jwtUtils; + + @Autowired + private UserDetailsServiceImpl userDetailsService; + + private static final Logger logger = LoggerFactory.getLogger(AuthTokenFilter.class); + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + try { + String jwt = parseJwt(request); + if (jwt != null && jwtUtils.validateJwtToken(jwt)) { + String username = jwtUtils.getUserNameFromJwtToken(jwt); + + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( + userDetails, + null, + userDetails.getAuthorities() + ); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } + catch (Exception e) { + logger.error("Cannot set user authentication: {}", e.getMessage()); + } + + filterChain.doFilter(request, response); + } + + private String parseJwt(HttpServletRequest request) { + String headerAuth = request.getHeader("Authorization"); + + if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) { + return headerAuth.substring(7, headerAuth.length()); + } + return null; + } +} diff --git a/src/main/java/swg/security/jwt/JwtUtils.java b/src/main/java/swg/security/jwt/JwtUtils.java new file mode 100644 index 0000000..caeda46 --- /dev/null +++ b/src/main/java/swg/security/jwt/JwtUtils.java @@ -0,0 +1,55 @@ +package swg.security.jwt; + +import io.jsonwebtoken.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import swg.service.UserDetailsImpl; + +import java.util.Date; + +@Component +public class JwtUtils { + private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class); + + @Value("${swg.app.jwtSecret}") + private String jwtSecret; + + @Value("${swg.app.jwtExpirationMs}") + private int jwtExpirationMs; + + public String generateJwtToken(Authentication authentication) { + UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal(); + + return Jwts.builder() + .setSubject((userPrincipal.getUsername())) + .setIssuedAt(new Date()) + .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)) + .signWith(SignatureAlgorithm.HS256, jwtSecret) + .compact(); + } + + public String getUserNameFromJwtToken(String token) { + return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject(); + } + + public boolean validateJwtToken(String authToken) { + try { + Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken); + return true; + } catch (SignatureException e) { + logger.error("Invalid JWT signature: {}", e.getMessage()); + } catch (MalformedJwtException e) { + logger.error("Invalid JWT token: {}", e.getMessage()); + } catch (ExpiredJwtException e) { + logger.error("JWT token is expired: {}", e.getMessage()); + } catch (UnsupportedJwtException e) { + logger.error("JWT token is unsupported: {}", e.getMessage()); + } catch (IllegalArgumentException e) { + logger.error("JWT claims string is empty: {}", e.getMessage()); + } + return false; + } +} diff --git a/src/main/java/swg/service/AccountService.java b/src/main/java/swg/service/AccountService.java index 0373f39..5c0c94f 100644 --- a/src/main/java/swg/service/AccountService.java +++ b/src/main/java/swg/service/AccountService.java @@ -2,7 +2,7 @@ package swg.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import swg.dao.AccountDao; +import swg.repository.AccountDao; import swg.entity.Account; import java.util.List; diff --git a/src/main/java/swg/service/CityObjectService.java b/src/main/java/swg/service/CityObjectService.java index 1bca73e..4fab158 100644 --- a/src/main/java/swg/service/CityObjectService.java +++ b/src/main/java/swg/service/CityObjectService.java @@ -1,10 +1,8 @@ package swg.service; -import org.hibernate.mapping.Property; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import swg.dao.CityObjectDao; -import swg.dao.PropertyListDao; +import swg.repository.CityObjectDao; import swg.entity.CityObject; import swg.entity.PropertyList; diff --git a/src/main/java/swg/service/ClockService.java b/src/main/java/swg/service/ClockService.java index 531258d..0305023 100644 --- a/src/main/java/swg/service/ClockService.java +++ b/src/main/java/swg/service/ClockService.java @@ -2,7 +2,7 @@ package swg.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import swg.dao.ClockDao; +import swg.repository.ClockDao; import swg.entity.Clock; import java.util.List; diff --git a/src/main/java/swg/service/MarketAuctionService.java b/src/main/java/swg/service/MarketAuctionService.java index 5f94871..45c5d70 100644 --- a/src/main/java/swg/service/MarketAuctionService.java +++ b/src/main/java/swg/service/MarketAuctionService.java @@ -2,7 +2,7 @@ package swg.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import swg.dao.MarketAuctionDao; +import swg.repository.MarketAuctionDao; import swg.entity.MarketAuction; import java.util.List; diff --git a/src/main/java/swg/service/PlayerService.java b/src/main/java/swg/service/PlayerService.java index be22826..0f5a953 100644 --- a/src/main/java/swg/service/PlayerService.java +++ b/src/main/java/swg/service/PlayerService.java @@ -2,10 +2,10 @@ package swg.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import swg.dao.ObjectsDao; -import swg.dao.PlayerDao; -import swg.dao.PlayerObjectDao; -import swg.dao.PropertyListDao; +import swg.repository.ObjectsDao; +import swg.repository.PlayerDao; +import swg.repository.PlayerObjectDao; +import swg.repository.PropertyListDao; import swg.entity.Objects; import swg.entity.Player; import swg.entity.PlayerObject; diff --git a/src/main/java/swg/service/PropertyListService.java b/src/main/java/swg/service/PropertyListService.java index 0220232..7e822cd 100644 --- a/src/main/java/swg/service/PropertyListService.java +++ b/src/main/java/swg/service/PropertyListService.java @@ -2,10 +2,8 @@ package swg.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import swg.dao.PropertyListDao; -import swg.dao.ResourceTypeDao; +import swg.repository.PropertyListDao; import swg.entity.PropertyList; -import swg.entity.ResourceType; import java.util.List; diff --git a/src/main/java/swg/service/ResourceTypeService.java b/src/main/java/swg/service/ResourceTypeService.java index 4e51cd6..c4edb13 100644 --- a/src/main/java/swg/service/ResourceTypeService.java +++ b/src/main/java/swg/service/ResourceTypeService.java @@ -2,7 +2,7 @@ package swg.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import swg.dao.ResourceTypeDao; +import swg.repository.ResourceTypeDao; import swg.entity.ResourceType; import java.util.List; diff --git a/src/main/java/swg/service/UasUserDetailsService.java b/src/main/java/swg/service/UasUserDetailsService.java new file mode 100644 index 0000000..163bd0e --- /dev/null +++ b/src/main/java/swg/service/UasUserDetailsService.java @@ -0,0 +1,8 @@ +package swg.service; + +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +public interface UasUserDetailsService { + UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; +} diff --git a/src/main/java/swg/service/UserDetailsImpl.java b/src/main/java/swg/service/UserDetailsImpl.java new file mode 100644 index 0000000..b150461 --- /dev/null +++ b/src/main/java/swg/service/UserDetailsImpl.java @@ -0,0 +1,122 @@ +package swg.service; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import swg.entity.UasUser; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +public class UserDetailsImpl implements UserDetails { + private static final long serialVersionUID = 1L; + + private Long id; + + private String username; + + private String email; + + @JsonIgnore + private String password; + + private Collection authorities; + + public UserDetailsImpl(Long id, String username, String email, String password, Collection authorities) { + this.id = id; + this.username = username; + this.email = email; + this.password = password; + this.authorities = authorities; + } + + public static UserDetailsImpl build(UasUser user) { + List authorities = new ArrayList<>(); + authorities.add(new SimpleGrantedAuthority(user.getUserRole().name())); + + return new UserDetailsImpl( + user.getId(), + user.getUsername(), + user.getEmail(), + user.getPassword(), + authorities + ); + } + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public Collection getAuthorities() { + return authorities; + } + + public void setAuthorities(Collection authorities) { + this.authorities = authorities; + } + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + UserDetailsImpl user = (UserDetailsImpl) o; + return Objects.equals(id, user.id); + } +} diff --git a/src/main/java/swg/service/UserDetailsServiceImpl.java b/src/main/java/swg/service/UserDetailsServiceImpl.java new file mode 100644 index 0000000..aaf12c6 --- /dev/null +++ b/src/main/java/swg/service/UserDetailsServiceImpl.java @@ -0,0 +1,27 @@ +package swg.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import swg.entity.UasUser; +import swg.repository.UasUserDao; + +import javax.transaction.Transactional; + +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + @Autowired + UasUserDao uasUserDao; + + @Override + @Transactional + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + UasUser user = uasUserDao.findByUsername(username) + .orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username)); + + return UserDetailsImpl.build(user); + } + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a923f3a..2d82172 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -11,3 +11,6 @@ spring.jpa.show-sql=true spring.application.name=swg-api spring.config.import=optional:swg-defaults.yml,optional:file:./swg-local.yml swg.log.config.path=./exe/linux/ +## JWT - Secret plus 24 hour expiration time (86400000) +swg.app.jwtSecret=smcisbestmc +swg.app.jwtExpirationMs=86400000