├── README.md ├── Spring Security ile JWT Entegrasyonu.pdf ├── pom.xml └── src └── main ├── java └── com │ └── demo │ └── app │ ├── Application.java │ ├── controller │ ├── AuthenticationController.java │ └── UserController.java │ ├── model │ ├── AuthenticationResponse.java │ └── UserContext.java │ ├── security │ ├── annotation │ │ └── IdGuard.java │ ├── aspect │ │ └── IdGuardAspect.java │ ├── config │ │ └── ApiSecurityConfig.java │ ├── entrypoint │ │ └── fail │ │ │ └── AuthenticationFailureEntryPoint.java │ ├── jwt │ │ ├── filter │ │ │ └── JwtAuthorizationFilter.java │ │ └── manager │ │ │ ├── EncryptionManager.java │ │ │ └── JwtTokenManager.java │ ├── provider │ │ └── UserAuthenticationProvider.java │ └── service │ │ └── userdetails │ │ └── AuthenticationUserDetailsService.java │ ├── service │ ├── AuthenticationService.java │ └── UserService.java │ └── swagger │ └── Swagger2Configuration.java └── resources └── application.properties /README.md: -------------------------------------------------------------------------------- 1 | # spring-security-jwt-integration 2 | JWT Integration Demo App with Spring Security 3 | 4 | Etkinlik linki :) 5 | 6 | https://www.youtube.com/watch?v=7ej-cWEGKwc 7 | -------------------------------------------------------------------------------- /Spring Security ile JWT Entegrasyonu.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/batux/spring-security-jwt-integration/e5d4943d0cf2af855aee83a7c9fe6d78ceea3dd7/Spring Security ile JWT Entegrasyonu.pdf -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.example 8 | spring-security-jwt-demo 9 | 1.0-SNAPSHOT 10 | 11 | 12 | org.springframework.boot 13 | spring-boot-starter-parent 14 | 2.4.0 15 | 16 | 17 | 18 | 11 19 | 11 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-web 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-aop 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-security 39 | 40 | 41 | io.jsonwebtoken 42 | jjwt 43 | 0.9.1 44 | 45 | 46 | commons-codec 47 | commons-codec 48 | 1.11 49 | 50 | 51 | 52 | io.springfox 53 | springfox-swagger2 54 | 2.9.2 55 | 56 | 57 | io.springfox 58 | springfox-swagger-ui 59 | 2.9.2 60 | 61 | 62 | org.projectlombok 63 | lombok 64 | 65 | 66 | javax.xml.bind 67 | jaxb-api 68 | 2.3.1 69 | 70 | 71 | 72 | 73 | spring-security-jwt-demo 74 | 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-maven-plugin 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/Application.java: -------------------------------------------------------------------------------- 1 | package com.demo.app; 2 | 3 | import com.demo.app.security.config.ApiSecurityConfig; 4 | import com.demo.app.swagger.Swagger2Configuration; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.context.annotation.Import; 8 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 9 | 10 | @EnableSwagger2 11 | @SpringBootApplication 12 | @Import( { ApiSecurityConfig.class, Swagger2Configuration.class } ) 13 | public class Application { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(Application.class, args); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/controller/AuthenticationController.java: -------------------------------------------------------------------------------- 1 | package com.demo.app.controller; 2 | 3 | import com.demo.app.model.AuthenticationResponse; 4 | import com.demo.app.service.AuthenticationService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.http.HttpHeaders; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.security.authentication.BadCredentialsException; 10 | import org.springframework.web.bind.annotation.RequestHeader; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RequestMethod; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | @RestController 16 | @RequestMapping(path = "/rest/api/v1") 17 | public class AuthenticationController { 18 | 19 | @Autowired 20 | private AuthenticationService authenticationService; 21 | 22 | 23 | @RequestMapping(path = "/login", method = RequestMethod.POST) 24 | public ResponseEntity login(@RequestHeader(HttpHeaders.AUTHORIZATION) String authorization) { 25 | try { 26 | 27 | AuthenticationResponse response = authenticationService.authenticate(authorization); 28 | return ResponseEntity.ok() 29 | .header(HttpHeaders.AUTHORIZATION, response.getToken()) 30 | .body(response); 31 | 32 | } catch (BadCredentialsException ex) { 33 | return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.demo.app.controller; 2 | 3 | import com.demo.app.model.UserContext; 4 | import com.demo.app.security.annotation.IdGuard; 5 | import com.demo.app.service.UserService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | @RestController 11 | @RequestMapping(path = "/rest/api/v1/user") 12 | public class UserController { 13 | 14 | @Autowired 15 | private UserService userService; 16 | 17 | 18 | @RequestMapping(method = RequestMethod.POST) 19 | public ResponseEntity register(@RequestBody UserContext userContext) { 20 | 21 | return ResponseEntity.ok().body(userService.save(userContext)); 22 | } 23 | 24 | @IdGuard(parameterIndex = 0) 25 | @RequestMapping(path = "/{userId}", method = RequestMethod.GET) 26 | public ResponseEntity load(@PathVariable("userId") Integer userId) { 27 | 28 | return ResponseEntity.ok().body(userService.load(userId)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/model/AuthenticationResponse.java: -------------------------------------------------------------------------------- 1 | package com.demo.app.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | @Data 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class AuthenticationResponse implements Serializable { 13 | 14 | private static final long serialVersionUID = 8929499253726531083L; 15 | 16 | private Integer userId; 17 | private String token; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/model/UserContext.java: -------------------------------------------------------------------------------- 1 | package com.demo.app.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | @Data 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class UserContext implements Serializable { 13 | 14 | private static final long serialVersionUID = -8099349063069095844L; 15 | 16 | private Integer id; 17 | private String name; 18 | private String surname; 19 | private String email; 20 | private String password; 21 | private String type; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/security/annotation/IdGuard.java: -------------------------------------------------------------------------------- 1 | package com.demo.app.security.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.METHOD) 10 | public @interface IdGuard { 11 | 12 | int parameterIndex(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/security/aspect/IdGuardAspect.java: -------------------------------------------------------------------------------- 1 | package com.demo.app.security.aspect; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | 5 | import com.demo.app.security.annotation.IdGuard; 6 | import com.demo.app.security.jwt.manager.EncryptionManager; 7 | import com.demo.app.security.jwt.manager.JwtTokenManager; 8 | import org.aspectj.lang.JoinPoint; 9 | import org.aspectj.lang.annotation.Aspect; 10 | import org.aspectj.lang.annotation.Before; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Component; 13 | import org.springframework.util.StringUtils; 14 | import org.springframework.web.context.request.RequestContextHolder; 15 | import org.springframework.web.context.request.ServletRequestAttributes; 16 | 17 | @Aspect 18 | @Component 19 | public class IdGuardAspect { 20 | 21 | @Autowired 22 | private JwtTokenManager tokenManager; 23 | 24 | @Autowired 25 | private EncryptionManager encryptionManager; 26 | 27 | 28 | @Before("@annotation(idGuard)") 29 | public void execute(JoinPoint joinPoint, IdGuard idGuard) { 30 | 31 | int argIndex = idGuard.parameterIndex(); 32 | if(argIndex < 0){ 33 | argIndex = 0; 34 | } 35 | 36 | Object[] args = joinPoint.getArgs(); 37 | if(args == null || args.length == 0) { 38 | throw new RuntimeException("ACCESS ERROR FOR INVALID RESOURCE"); 39 | } 40 | 41 | Object idParameterObj = args[argIndex]; 42 | if(idParameterObj == null) { 43 | throw new RuntimeException("ACCESS ERROR FOR INVALID RESOURCE"); 44 | } 45 | 46 | String expectedTicket = encryptionManager.encrypt(idParameterObj.toString()); 47 | 48 | HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); 49 | String token = tokenManager.extractJwtFromRequest(request); 50 | if(!StringUtils.hasText(token)) { 51 | throw new RuntimeException("ACCESS ERROR FOR INVALID RESOURCE"); 52 | } 53 | 54 | String realTicket = tokenManager.extractTicket(token); 55 | if(!(StringUtils.hasText(expectedTicket) && expectedTicket.contains(realTicket)) ) { 56 | throw new RuntimeException("ACCESS ERROR FOR INVALID RESOURCE"); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/security/config/ApiSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.demo.app.security.config; 2 | 3 | import com.demo.app.security.entrypoint.fail.AuthenticationFailureEntryPoint; 4 | import com.demo.app.security.jwt.filter.JwtAuthorizationFilter; 5 | import com.demo.app.security.jwt.manager.JwtTokenManager; 6 | import com.demo.app.security.provider.UserAuthenticationProvider; 7 | import com.demo.app.security.service.userdetails.AuthenticationUserDetailsService; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.core.annotation.Order; 12 | import org.springframework.http.HttpMethod; 13 | import org.springframework.security.authentication.AuthenticationManager; 14 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 15 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 16 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 17 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 18 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 19 | import org.springframework.security.config.http.SessionCreationPolicy; 20 | import org.springframework.security.crypto.password.NoOpPasswordEncoder; 21 | import org.springframework.web.cors.CorsConfiguration; 22 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 23 | import org.springframework.web.filter.CorsFilter; 24 | 25 | @Configuration 26 | @EnableWebSecurity 27 | @Order(1) 28 | public class ApiSecurityConfig extends WebSecurityConfigurerAdapter { 29 | 30 | @Autowired 31 | private AuthenticationFailureEntryPoint failureEntryPoint; 32 | 33 | @Autowired 34 | private JwtTokenManager tokenManager; 35 | 36 | @Autowired 37 | private AuthenticationUserDetailsService authenticationUserDetailsService; 38 | 39 | 40 | @Override 41 | public void configure(HttpSecurity http) throws Exception { 42 | 43 | http = http.cors().and().csrf().disable(); 44 | 45 | http = http 46 | .sessionManagement() 47 | .sessionCreationPolicy(SessionCreationPolicy.STATELESS) 48 | .and(); 49 | 50 | http = http 51 | .exceptionHandling() 52 | .authenticationEntryPoint(failureEntryPoint) 53 | .and(); 54 | 55 | http.authorizeRequests() 56 | .antMatchers("/v2/api-docs", "/api-docs", 57 | "/configuration/ui", "/configuration/security", 58 | "/swagger-ui/**", "/swagger-resources/**", "/swagger-ui.html", "/webjars/**").permitAll() 59 | .antMatchers(HttpMethod.POST, "/rest/api/v1/login").permitAll() 60 | .antMatchers(HttpMethod.POST, "/rest/api/v1/user").permitAll() 61 | .anyRequest().authenticated() 62 | .and() 63 | .addFilter(new JwtAuthorizationFilter(authenticationManager(), tokenManager)); 64 | } 65 | 66 | @Override 67 | protected void configure(AuthenticationManagerBuilder auth) { 68 | 69 | UserAuthenticationProvider authenticationProvider = new UserAuthenticationProvider(); 70 | authenticationProvider.setPasswordEncoder(NoOpPasswordEncoder.getInstance()); 71 | authenticationProvider.setUserDetailsService(authenticationUserDetailsService); 72 | auth.authenticationProvider(authenticationProvider); 73 | } 74 | 75 | @Bean 76 | @Override 77 | public AuthenticationManager authenticationManagerBean() throws Exception { 78 | return super.authenticationManagerBean(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/security/entrypoint/fail/AuthenticationFailureEntryPoint.java: -------------------------------------------------------------------------------- 1 | package com.demo.app.security.entrypoint.fail; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.ServletException; 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 AuthenticationFailureEntryPoint implements AuthenticationEntryPoint { 15 | 16 | @Override 17 | public void commence(HttpServletRequest request, HttpServletResponse response, 18 | AuthenticationException authException) throws IOException, ServletException { 19 | 20 | response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); 21 | response.addHeader("INVALID-AUTH", "Invalid authentication attempt!"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/security/jwt/filter/JwtAuthorizationFilter.java: -------------------------------------------------------------------------------- 1 | package com.demo.app.security.jwt.filter; 2 | 3 | import javax.servlet.FilterChain; 4 | import javax.servlet.http.HttpServletRequest; 5 | import javax.servlet.http.HttpServletResponse; 6 | 7 | import com.demo.app.security.jwt.manager.JwtTokenManager; 8 | import org.springframework.http.HttpHeaders; 9 | import org.springframework.security.authentication.AuthenticationManager; 10 | import org.springframework.security.authentication.BadCredentialsException; 11 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 12 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 13 | import org.springframework.security.core.context.SecurityContextHolder; 14 | import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; 15 | import org.springframework.util.StringUtils; 16 | 17 | import io.jsonwebtoken.ExpiredJwtException; 18 | 19 | import java.util.List; 20 | 21 | public class JwtAuthorizationFilter extends BasicAuthenticationFilter { 22 | 23 | private JwtTokenManager tokenManager; 24 | 25 | public JwtAuthorizationFilter(AuthenticationManager authenticationManager, JwtTokenManager tokenManager) { 26 | super(authenticationManager); 27 | this.tokenManager = tokenManager; 28 | } 29 | 30 | @Override 31 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { 32 | 33 | try { 34 | 35 | String jwtToken = tokenManager.extractJwtFromRequest(request); 36 | if(jwtToken == null) { 37 | chain.doFilter(request, response); 38 | return; 39 | } 40 | 41 | if(StringUtils.hasText(jwtToken) && tokenManager.validate(jwtToken)) { 42 | 43 | String principal = tokenManager.getUsernameFromToken(jwtToken); 44 | List roles = tokenManager.getRolesFromToken(jwtToken); 45 | 46 | UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( 47 | principal, null, roles); 48 | 49 | SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); 50 | chain.doFilter(request, response); 51 | return; 52 | } 53 | else { 54 | if(isBasicAuthRequest(request)) { 55 | SecurityContextHolder.clearContext(); 56 | chain.doFilter(request, response); 57 | return; 58 | } 59 | prepareInvalidAuthResponse(response); 60 | return; 61 | } 62 | } 63 | catch (ExpiredJwtException | BadCredentialsException ex) { 64 | prepareInvalidAuthResponse(response); 65 | return; 66 | } 67 | catch(Exception ex) { 68 | prepareInvalidAuthResponse(response); 69 | return; 70 | } 71 | } 72 | 73 | private boolean isBasicAuthRequest(HttpServletRequest request) { 74 | String data = request.getHeader(HttpHeaders.AUTHORIZATION); 75 | return (StringUtils.hasText(data) && data.startsWith("Basic ")); 76 | } 77 | 78 | private void prepareInvalidAuthResponse(HttpServletResponse response) { 79 | SecurityContextHolder.clearContext(); 80 | response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); 81 | response.addHeader("INVALID-AUTH", "Invalid authentication attempt!"); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/security/jwt/manager/EncryptionManager.java: -------------------------------------------------------------------------------- 1 | package com.demo.app.security.jwt.manager; 2 | 3 | import org.apache.commons.codec.digest.DigestUtils; 4 | import org.springframework.stereotype.Component; 5 | import org.springframework.util.StringUtils; 6 | 7 | @Component 8 | public class EncryptionManager { 9 | 10 | private final String salt = "DEVTECH-SECRET*!>>"; 11 | 12 | public String encrypt(String payload) { 13 | 14 | if(!StringUtils.hasText(payload)) { 15 | return ""; 16 | } 17 | return DigestUtils.sha256Hex(payload + salt); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/security/jwt/manager/JwtTokenManager.java: -------------------------------------------------------------------------------- 1 | package com.demo.app.security.jwt.manager; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.Date; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import javax.servlet.http.HttpServletRequest; 11 | 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.beans.factory.annotation.Value; 14 | import org.springframework.http.HttpHeaders; 15 | import org.springframework.security.authentication.BadCredentialsException; 16 | import org.springframework.security.core.GrantedAuthority; 17 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 18 | import org.springframework.security.core.userdetails.UserDetails; 19 | import org.springframework.stereotype.Component; 20 | import org.springframework.util.StringUtils; 21 | 22 | import io.jsonwebtoken.Claims; 23 | import io.jsonwebtoken.ExpiredJwtException; 24 | import io.jsonwebtoken.Jwts; 25 | import io.jsonwebtoken.MalformedJwtException; 26 | import io.jsonwebtoken.SignatureAlgorithm; 27 | import io.jsonwebtoken.SignatureException; 28 | import io.jsonwebtoken.UnsupportedJwtException; 29 | import lombok.extern.slf4j.Slf4j; 30 | 31 | @Slf4j 32 | @Component 33 | public class JwtTokenManager { 34 | 35 | @Value("${jwt.token.secret}") 36 | private String secret; 37 | 38 | @Value("${jwt.token.expiration.duration}") 39 | private long expirationDurationInMs; 40 | 41 | @Autowired 42 | private EncryptionManager encryptionManager; 43 | 44 | 45 | public String generateToken(UserDetails userDetails, Integer userId) { 46 | 47 | Map claims = new HashMap<>(); 48 | 49 | Collection roles = userDetails.getAuthorities(); 50 | 51 | if (roles.contains(new SimpleGrantedAuthority("ADMIN"))) { 52 | claims.put("isAdmin", true); 53 | } 54 | 55 | if (roles.contains(new SimpleGrantedAuthority("USER"))) { 56 | claims.put("isUser", true); 57 | } 58 | 59 | if (userId != null && userId > 0) { 60 | claims.put("ticket", encryptionManager.encrypt(String.valueOf(userId))); 61 | } 62 | 63 | return doGenerateToken(claims, userDetails.getUsername()); 64 | } 65 | 66 | public boolean validate(String authToken) { 67 | 68 | try { 69 | Jwts.parser().setSigningKey(secret).parseClaimsJws(authToken); 70 | return true; 71 | } 72 | catch (SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException ex) { 73 | throw new BadCredentialsException("INVALID_CREDENTIALS", ex); 74 | } 75 | catch (ExpiredJwtException ex) { 76 | throw ex; 77 | } 78 | } 79 | 80 | public String getUsernameFromToken(String token) { 81 | 82 | Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); 83 | return claims.getSubject(); 84 | } 85 | 86 | public List getRolesFromToken(String token) { 87 | 88 | Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); 89 | List roles = new ArrayList(2); 90 | 91 | Boolean admin = claims.get("isAdmin", Boolean.class); 92 | if (admin != null && admin) { 93 | roles.add(new SimpleGrantedAuthority("ADMIN")); 94 | } 95 | 96 | Boolean user = claims.get("isUser", Boolean.class); 97 | if (user != null && user) { 98 | roles.add(new SimpleGrantedAuthority("USER")); 99 | } 100 | return roles; 101 | } 102 | 103 | public String extractTicket(String token) { 104 | 105 | Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); 106 | return claims.get("ticket", String.class); 107 | } 108 | 109 | public String extractJwtFromRequest(HttpServletRequest request) { 110 | String data = request.getHeader(HttpHeaders.AUTHORIZATION); 111 | if (StringUtils.hasText(data) && data.startsWith("Bearer ")) { 112 | return data.substring(7, data.length()); 113 | } 114 | return null; 115 | } 116 | 117 | private String doGenerateToken(Map claims, String subject) { 118 | 119 | long currentTimeMillis = System.currentTimeMillis(); 120 | return Jwts.builder() 121 | .setClaims(claims) 122 | .setSubject(subject) 123 | .setIssuedAt(new Date(currentTimeMillis)) 124 | .setExpiration(new Date(currentTimeMillis + expirationDurationInMs)) 125 | .signWith(SignatureAlgorithm.HS512, secret) 126 | .compact(); 127 | 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/security/provider/UserAuthenticationProvider.java: -------------------------------------------------------------------------------- 1 | package com.demo.app.security.provider; 2 | 3 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 4 | import org.springframework.security.authentication.dao.DaoAuthenticationProvider; 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | 8 | public class UserAuthenticationProvider extends DaoAuthenticationProvider { 9 | 10 | @Override 11 | protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) { 12 | 13 | return new UsernamePasswordAuthenticationToken(principal, user.getPassword(), user.getAuthorities()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/security/service/userdetails/AuthenticationUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package com.demo.app.security.service.userdetails; 2 | 3 | import com.demo.app.model.UserContext; 4 | import com.demo.app.service.UserService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.security.core.userdetails.User; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | import org.springframework.security.core.userdetails.UserDetailsService; 9 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 10 | import org.springframework.stereotype.Component; 11 | 12 | @Component 13 | public class AuthenticationUserDetailsService implements UserDetailsService { 14 | 15 | @Autowired 16 | private UserService userService; 17 | 18 | @Override 19 | public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { 20 | 21 | UserContext userContext = userService.load(email); 22 | if(userContext == null) { 23 | throw new UsernameNotFoundException("User not found!"); 24 | } 25 | return User.withUsername(userContext.getEmail()) 26 | .password(userContext.getPassword()) 27 | .authorities("USER") 28 | .build(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/service/AuthenticationService.java: -------------------------------------------------------------------------------- 1 | package com.demo.app.service; 2 | 3 | import com.demo.app.model.AuthenticationResponse; 4 | import com.demo.app.model.UserContext; 5 | import com.demo.app.security.jwt.manager.JwtTokenManager; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.security.authentication.AuthenticationManager; 8 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 9 | import org.springframework.security.core.Authentication; 10 | import org.springframework.security.core.userdetails.User; 11 | 12 | import org.springframework.stereotype.Service; 13 | import org.springframework.util.StringUtils; 14 | 15 | import java.nio.charset.StandardCharsets; 16 | import java.util.Base64; 17 | 18 | 19 | @Service 20 | public class AuthenticationService { 21 | 22 | @Autowired 23 | private UserService userService; 24 | 25 | @Autowired 26 | private AuthenticationManager authenticationManager; 27 | 28 | @Autowired 29 | private JwtTokenManager tokenManager; 30 | 31 | 32 | public AuthenticationResponse authenticate(String authorization) { 33 | 34 | if(!StringUtils.hasText(authorization)) { 35 | throw new RuntimeException("INVALID AUTH PAYLOAD"); 36 | } 37 | 38 | String[] httpBasicAuthPayload = parseHttpBasicPayload(authorization); 39 | String email = httpBasicAuthPayload[0]; 40 | String password = httpBasicAuthPayload[1]; 41 | 42 | Authentication authenticate = authenticationManager 43 | .authenticate( 44 | new UsernamePasswordAuthenticationToken(email, password) 45 | ); 46 | 47 | User user = (User) authenticate.getPrincipal(); 48 | if(user == null) { 49 | throw new RuntimeException("USER NOT FOUND"); 50 | } 51 | 52 | UserContext userContext = userService.load(email); 53 | String jwtToken = tokenManager.generateToken(user, userContext.getId()); 54 | return new AuthenticationResponse(userContext.getId(), jwtToken); 55 | } 56 | 57 | private String[] parseHttpBasicPayload(String authorization) { 58 | 59 | if (authorization != null && authorization.toLowerCase().startsWith("basic")) { 60 | 61 | String base64Credentials = authorization.substring("Basic".length()).trim(); 62 | byte[] credDecoded = Base64.getDecoder().decode(base64Credentials); 63 | String credentials = new String(credDecoded, StandardCharsets.UTF_8); 64 | return credentials.split(":", 2); 65 | } 66 | return null; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.demo.app.service; 2 | 3 | import com.demo.app.model.UserContext; 4 | import org.springframework.stereotype.Service; 5 | 6 | import javax.annotation.PostConstruct; 7 | import java.util.*; 8 | 9 | @Service 10 | public class UserService { 11 | 12 | private final Object mutex = new Object(); 13 | private List userContextList = Collections.synchronizedList(new ArrayList<>()); 14 | 15 | @PostConstruct 16 | public void init() { 17 | 18 | userContextList.add(new UserContext(1, "Mustafa Kemal", "Atatürk", "mustafa.kemal.ataturk@turkiye.com.tr", "1881", "ADMIN")); 19 | userContextList.add(new UserContext(2, "Hasan Ali", "Yücel", "hasan.ali.yucel@turkiye.com.tr", "1897", "ADMIN")); 20 | } 21 | 22 | public Integer save(UserContext userContext) { 23 | 24 | synchronized (mutex) { 25 | Integer userId = determineMaxUserId(); 26 | userContext.setId(userId); 27 | userContextList.add(userContext); 28 | } 29 | return userContext.getId(); 30 | } 31 | 32 | public UserContext load(String email) { 33 | 34 | synchronized (mutex) { 35 | return userContextList.stream().filter(u -> u.getEmail().equals(email)).findFirst().orElse(null); 36 | } 37 | } 38 | 39 | public UserContext load(Integer id) { 40 | 41 | synchronized (mutex) { 42 | return userContextList.stream().filter(u -> u.getId().equals(id)).findFirst().orElse(null); 43 | } 44 | } 45 | 46 | private Integer determineMaxUserId() { 47 | 48 | int maxUserId = 1; 49 | UserContext lastUserContext = userContextList.stream().max(Comparator.comparing(UserContext::getId)).orElse(null); 50 | if(lastUserContext != null) { 51 | maxUserId = lastUserContext.getId() + 1; 52 | } 53 | return maxUserId; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/demo/app/swagger/Swagger2Configuration.java: -------------------------------------------------------------------------------- 1 | package com.demo.app.swagger; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.core.annotation.Order; 9 | 10 | import springfox.documentation.builders.ApiInfoBuilder; 11 | import springfox.documentation.builders.PathSelectors; 12 | import springfox.documentation.builders.RequestHandlerSelectors; 13 | import springfox.documentation.service.ApiInfo; 14 | import springfox.documentation.service.ApiKey; 15 | import springfox.documentation.service.AuthorizationScope; 16 | import springfox.documentation.service.Contact; 17 | import springfox.documentation.service.SecurityReference; 18 | import springfox.documentation.spi.DocumentationType; 19 | import springfox.documentation.spi.service.contexts.SecurityContext; 20 | import springfox.documentation.spring.web.plugins.Docket; 21 | 22 | @Configuration 23 | @Order(2) 24 | public class Swagger2Configuration { 25 | 26 | @Bean 27 | public Docket api() { 28 | 29 | return new Docket(DocumentationType.SWAGGER_2) 30 | .apiInfo(apiEndPointsInfo()) 31 | .securityContexts(Arrays.asList(securityContext())) 32 | .securitySchemes(Arrays.asList(apiKey())) 33 | .select() 34 | .apis(RequestHandlerSelectors 35 | .basePackage("com.demo.app.controller")) 36 | 37 | .paths(PathSelectors.any()) 38 | .build(); 39 | } 40 | 41 | private ApiInfo apiEndPointsInfo() { 42 | 43 | return new ApiInfoBuilder().title("Demo REST API Layer") 44 | .description("Demo REST API Layer") 45 | .contact(new Contact("Batuhan Düzgün", "www.demo.com", "batuhan.duzgun@windowslive.com")) 46 | .license("Apache 2.0") 47 | .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html") 48 | .version("1.0") 49 | .build(); 50 | } 51 | 52 | private ApiKey apiKey() { 53 | 54 | return new ApiKey("JWT", "Authorization", "header"); 55 | } 56 | 57 | private SecurityContext securityContext() { 58 | 59 | return SecurityContext.builder().securityReferences(defaultAuth()).build(); 60 | } 61 | 62 | private List defaultAuth() { 63 | 64 | AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); 65 | AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; 66 | authorizationScopes[0] = authorizationScope; 67 | return Arrays.asList(new SecurityReference("JWT", authorizationScopes)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | server.port=9090 3 | 4 | jwt.token.secret=devtech 5 | jwt.token.expiration.duration=18000000 6 | --------------------------------------------------------------------------------