-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
697 additions
and
0 deletions.
There are no files selected for viewing
82 changes: 82 additions & 0 deletions
82
code/backend/src/main/java/app/security/RestSecurityConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package app.security; | ||
|
||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.core.annotation.Order; | ||
import org.springframework.http.HttpMethod; | ||
import org.springframework.security.authentication.AuthenticationManager; | ||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | ||
import org.springframework.security.config.http.SessionCreationPolicy; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
|
||
import app.security.jwt.JwtRequestFilter; | ||
|
||
|
||
@Configuration | ||
@Order(1) | ||
public class RestSecurityConfig extends WebSecurityConfigurerAdapter { | ||
|
||
@Autowired | ||
RepositoryUserDetailsService userDetailsService; | ||
|
||
@Autowired | ||
private PasswordEncoder passwordEncoder; | ||
|
||
@Autowired | ||
private JwtRequestFilter jwtRequestFilter; | ||
|
||
@Override | ||
protected void configure(AuthenticationManagerBuilder auth) throws Exception { | ||
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder); | ||
} | ||
|
||
//Expose AuthenticationManager as a Bean to be used in other services | ||
@Bean | ||
@Override | ||
public AuthenticationManager authenticationManagerBean() throws Exception { | ||
return super.authenticationManagerBean(); | ||
} | ||
|
||
@Override | ||
protected void configure(HttpSecurity http) throws Exception { | ||
http.antMatcher("/api/**"); | ||
|
||
http.authorizeRequests().antMatchers(HttpMethod.POST, "/api/games/").hasRole("ADMIN"); | ||
http.authorizeRequests().antMatchers(HttpMethod.DELETE, "/api/games/**").hasRole("ADMIN"); | ||
http.authorizeRequests().antMatchers(HttpMethod.PUT, "/api/games/**").hasRole("ADMIN"); | ||
|
||
http.authorizeRequests().antMatchers(HttpMethod.DELETE, "/api/reviews/**").hasRole("USER"); | ||
http.authorizeRequests().antMatchers(HttpMethod.POST, "/api/reviews/**/**").hasRole("USER"); | ||
|
||
http.authorizeRequests().antMatchers(HttpMethod.GET, "/api/users/me").hasRole("USER"); | ||
http.authorizeRequests().antMatchers(HttpMethod.GET, "/api/users/**/moreCartGames/**").hasRole("USER"); | ||
http.authorizeRequests().antMatchers(HttpMethod.GET, "/api/users/**/cart").hasRole("USER"); | ||
http.authorizeRequests().antMatchers(HttpMethod.POST, "/api/users/**/cart/**").hasRole("USER"); | ||
http.authorizeRequests().antMatchers(HttpMethod.DELETE, "/api/users/**/cart/**").hasRole("USER"); | ||
http.authorizeRequests().antMatchers(HttpMethod.POST, "/api/users/**/checkout").hasRole("USER"); | ||
http.authorizeRequests().antMatchers(HttpMethod.PUT, "/api/users/**").hasRole("USER"); | ||
|
||
|
||
// Other URLs can be accessed without authentication | ||
http.authorizeRequests().anyRequest().permitAll(); | ||
|
||
// Disable CSRF protection (it is difficult to implement in REST APIs) | ||
http.csrf().disable(); | ||
|
||
// Disable Http Basic Authentication | ||
http.httpBasic().disable(); | ||
|
||
// Disable Form login Authentication | ||
http.formLogin().disable(); | ||
|
||
// Avoid creating session | ||
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); | ||
|
||
// Add JWT Token filter | ||
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
code/backend/src/main/java/app/security/jwt/AuthResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package app.security.jwt; | ||
|
||
public class AuthResponse { | ||
|
||
private Status status; | ||
private String message; | ||
private String error; | ||
|
||
public enum Status { | ||
SUCCESS, FAILURE | ||
} | ||
|
||
public AuthResponse() { | ||
} | ||
|
||
public AuthResponse(Status status, String message) { | ||
this.status = status; | ||
this.message = message; | ||
} | ||
|
||
public AuthResponse(Status status, String message, String error) { | ||
this.status = status; | ||
this.message = message; | ||
this.error = error; | ||
} | ||
|
||
public Status getStatus() { | ||
return status; | ||
} | ||
|
||
public void setStatus(Status status) { | ||
this.status = status; | ||
} | ||
|
||
public String getMessage() { | ||
return message; | ||
} | ||
|
||
public void setMessage(String message) { | ||
this.message = message; | ||
} | ||
|
||
public String getError() { | ||
return error; | ||
} | ||
|
||
public void setError(String error) { | ||
this.error = error; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "LoginResponse [status=" + status + ", message=" + message + ", error=" + error + "]"; | ||
} | ||
|
||
} |
27 changes: 27 additions & 0 deletions
27
code/backend/src/main/java/app/security/jwt/JwtCookieManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package app.security.jwt; | ||
|
||
import org.springframework.http.HttpCookie; | ||
import org.springframework.http.ResponseCookie; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class JwtCookieManager { | ||
|
||
public static final String ACCESS_TOKEN_COOKIE_NAME = "AuthToken"; | ||
public static final String REFRESH_TOKEN_COOKIE_NAME = "RefreshToken"; | ||
|
||
public HttpCookie createAccessTokenCookie(String token, Long duration) { | ||
String encryptedToken = SecurityCipher.encrypt(token); | ||
return ResponseCookie.from(ACCESS_TOKEN_COOKIE_NAME, encryptedToken).maxAge(-1).httpOnly(true).path("/").build(); | ||
} | ||
|
||
public HttpCookie createRefreshTokenCookie(String token, Long duration) { | ||
String encryptedToken = SecurityCipher.encrypt(token); | ||
return ResponseCookie.from(REFRESH_TOKEN_COOKIE_NAME, encryptedToken).maxAge(-1).httpOnly(true).path("/").build(); | ||
} | ||
|
||
public HttpCookie deleteAccessTokenCookie() { | ||
return ResponseCookie.from(ACCESS_TOKEN_COOKIE_NAME, "").maxAge(0).httpOnly(true).path("/").build(); | ||
} | ||
|
||
} |
106 changes: 106 additions & 0 deletions
106
code/backend/src/main/java/app/security/jwt/JwtRequestFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package app.security.jwt; | ||
|
||
import java.io.IOException; | ||
|
||
import javax.servlet.FilterChain; | ||
import javax.servlet.ServletException; | ||
import javax.servlet.http.Cookie; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
|
||
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.core.userdetails.UserDetailsService; | ||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.util.StringUtils; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
|
||
@Component | ||
public class JwtRequestFilter extends OncePerRequestFilter { | ||
|
||
private static final Logger LOG = LoggerFactory.getLogger(JwtRequestFilter.class); | ||
|
||
@Autowired | ||
private UserDetailsService userDetailsService; | ||
|
||
@Autowired | ||
private JwtTokenProvider jwtTokenProvider; | ||
|
||
@Override | ||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, | ||
FilterChain filterChain) throws ServletException, IOException { | ||
|
||
try { | ||
String token = getJwtToken(request, true); | ||
|
||
if (StringUtils.hasText(token) && jwtTokenProvider.validateToken(token)) { | ||
|
||
String username = jwtTokenProvider.getUsername(token); | ||
|
||
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 ex) { | ||
LOG.error("Exception processing JWT Token",ex); | ||
} | ||
|
||
filterChain.doFilter(request, response); | ||
} | ||
|
||
private String getJwtToken(HttpServletRequest request, boolean fromCookie) { | ||
|
||
if (fromCookie) { | ||
return getJwtFromCookie(request); | ||
} else { | ||
return getJwtFromRequest(request); | ||
} | ||
} | ||
|
||
private String getJwtFromRequest(HttpServletRequest request) { | ||
|
||
String bearerToken = request.getHeader("Authorization"); | ||
|
||
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { | ||
|
||
String accessToken = bearerToken.substring(7); | ||
if (accessToken == null) { | ||
return null; | ||
} | ||
|
||
return SecurityCipher.decrypt(accessToken); | ||
} | ||
return null; | ||
} | ||
|
||
private String getJwtFromCookie(HttpServletRequest request) { | ||
|
||
Cookie[] cookies = request.getCookies(); | ||
|
||
if (cookies == null) { | ||
return ""; | ||
} | ||
|
||
for (Cookie cookie : cookies) { | ||
if (JwtCookieManager.ACCESS_TOKEN_COOKIE_NAME.equals(cookie.getName())) { | ||
String accessToken = cookie.getValue(); | ||
if (accessToken == null) { | ||
return null; | ||
} | ||
|
||
return SecurityCipher.decrypt(accessToken); | ||
} | ||
} | ||
return null; | ||
} | ||
} |
Oops, something went wrong.