Browse Source

JWT generation (1)

dagarcos 2 năm trước cách đây
mục cha
commit
3d9706f36f

+ 5 - 0
pom.xml

@@ -50,6 +50,11 @@
 		    <groupId>org.springframework.boot</groupId> 
 		    <artifactId>spring-boot-starter-validation</artifactId> 
 		</dependency>
+		<dependency>
+			<groupId>com.auth0</groupId>
+			<artifactId>java-jwt</artifactId>
+			<version>3.18.1</version>
+		</dependency>
 	</dependencies>
 
 	<build>

+ 35 - 6
src/main/java/es/uv/garcosda/config/WebSecurityConfig.java

@@ -1,23 +1,52 @@
 package es.uv.garcosda.config;
 
+import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
+
+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.authentication.ProviderManager;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.web.SecurityFilterChain;
 
+import es.uv.garcosda.security.CustomAuthenticationFilter;
+import es.uv.garcosda.security.CustomUserDetailsService;
+
 @Configuration
 @EnableWebSecurity
-public class WebSecurityConfig {
+public class WebSecurityConfig{	
+	
+	@Autowired
+    private CustomUserDetailsService userDetailsService;
+	
+	@Bean
+    public PasswordEncoder passwordEncoder() {
+        return new BCryptPasswordEncoder();
+    }
 	
 	@Bean
+	public AuthenticationManager authenticationManager(){
+	    DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
+	    authProvider.setUserDetailsService(userDetailsService);
+	    authProvider.setPasswordEncoder(passwordEncoder());
+	    return new ProviderManager(authProvider);
+	}
+		
+	@Bean
 	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
 		
-		http.authorizeHttpRequests()
-			.requestMatchers("/login").permitAll()
-		    .anyRequest().authenticated()
-		    .and()
-		    .httpBasic();
+		http.csrf().disable()
+			.cors().disable()
+			.sessionManagement().sessionCreationPolicy(STATELESS)
+			.and()
+			.authorizeHttpRequests().anyRequest().permitAll()
+			.and()
+			.addFilter(new CustomAuthenticationFilter(authenticationManager()));
 		
 		return http.build();
 	}

+ 61 - 0
src/main/java/es/uv/garcosda/security/CustomAuthenticationFilter.java

@@ -0,0 +1,61 @@
+package es.uv.garcosda.security;
+
+import java.io.IOException;
+import java.util.stream.Collectors;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+import es.uv.garcosda.services.JwtService;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter{
+
+	@Autowired
+	private AuthenticationManager authenticationManager;
+	
+	@Autowired
+	private JwtService jwtService;
+	
+	public CustomAuthenticationFilter(AuthenticationManager authenticationManager) { 
+		this.authenticationManager = authenticationManager;
+	}
+	
+	@Override
+	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
+		UsernamePasswordAuthenticationToken authtoken = new UsernamePasswordAuthenticationToken(request.getParameter("username"), 
+																								request.getParameter("password"));
+		return this.authenticationManager.authenticate(authtoken);
+	}
+
+	@Override
+	protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
+			Authentication auth) throws IOException, ServletException {
+		
+		User user = (User)auth.getPrincipal();
+		
+		String access_token = jwtService.generateAccessToken(user.getUsername(), 
+															 user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()));
+		String refresh_token = jwtService.generateRefreshToken(user.getUsername(), 
+															   user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()));
+
+		response.setHeader("access_token", access_token);
+		response.setHeader("refresh_token", refresh_token);
+	}
+
+	@Override
+	protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
+			AuthenticationException failed) throws IOException, ServletException {
+		super.unsuccessfulAuthentication(request, response, failed);
+	}
+	
+}

+ 43 - 0
src/main/java/es/uv/garcosda/security/CustomUserDetailsService.java

@@ -0,0 +1,43 @@
+package es.uv.garcosda.security;
+
+import java.util.Collection;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Component;
+
+import es.uv.garcosda.domain.User;
+import es.uv.garcosda.repositories.UserRepository;
+
+
+@Component
+public class CustomUserDetailsService implements UserDetailsService {
+
+	@Autowired 
+	UserRepository repo;
+	
+	@Override
+	public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
+		User user = repo.findByUsername(email).orElseThrow(() -> new UsernameNotFoundException("User not found"));
+		return new org.springframework.security.core.userdetails.User(user.getUsername(), 
+														              user.getPassword(),
+														              getAuthorities(user));
+    }
+
+	private static Collection<? extends GrantedAuthority> getAuthorities(User user) {
+        String[] userRoles = user.getRoles()
+                                 .stream()
+                                 .map((role) -> role.getName()).toArray(String[]::new);
+        
+        
+        System.out.println(userRoles);
+        Collection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(userRoles);
+        return authorities;
+    }
+
+}

+ 90 - 0
src/main/java/es/uv/garcosda/services/JwtService.java

@@ -0,0 +1,90 @@
+package es.uv.garcosda.services;
+
+import jakarta.annotation.PostConstruct;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.stereotype.Component;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.interfaces.DecodedJWT;
+
+@Component
+public class JwtService {
+
+	@Value("${sys.token.key}")
+	private String key;
+	
+	@Value("${sys.token.issuer}")
+	private String issuer;
+	
+	@Value("${sys.token.duration}")
+	private Integer duration;
+	
+	private Algorithm algorithm;
+	private JWTVerifier verifier;
+	
+	@PostConstruct
+	public void init(){
+		this.algorithm = Algorithm.HMAC256(this.key.getBytes());
+		this.verifier = JWT.require(this.algorithm).build();
+	}
+	
+	public String generateAccessToken(String username, List<String> claims) {
+		return JWT.create()
+				 .withSubject(username)
+				 .withExpiresAt(new Date(System.currentTimeMillis()+this.duration))
+				 .withIssuer(this.issuer)
+				 .withClaim("roles", claims)
+				 .sign(this.algorithm);
+	}
+	
+	public String generateRefreshToken(String username, List<String> claims) {
+		return JWT.create()
+				 .withSubject(username)
+				 .withExpiresAt(new Date(System.currentTimeMillis()+(this.duration*2)))
+				 .withIssuer(this.issuer)
+				 .withClaim("roles", claims)
+				 .sign(this.algorithm);
+	}
+	
+	public String getUsernameFromToken(String token) {
+		DecodedJWT decoded = this.verifier.verify(token);
+		return decoded.getSubject();
+	}
+	
+	public Collection<SimpleGrantedAuthority> getAuthoritiesFromToken(String token){
+		DecodedJWT decoded = this.verifier.verify(token);
+		String[] roles = decoded.getClaim("roles").asArray(String.class);
+		Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
+		for(String r : roles) { authorities.add(new SimpleGrantedAuthority(r)); }
+		return authorities;
+	}
+	
+	public String[] getRolesFromToken(String token){
+		DecodedJWT decoded = this.verifier.verify(token);
+		String[] roles = decoded.getClaim("roles").asArray(String.class);
+		return roles;
+	}
+	
+	public Boolean isTokenExpired(String token) {
+		DecodedJWT decoded = this.verifier.verify(token);
+        final Date expiration = decoded.getExpiresAt();
+        return expiration.before(new Date());
+    }
+	
+	public String getTokenFromHeader(String header) {
+		return header.substring(this.getTokenHeaderPrefix().length());
+	}
+	
+	public String getTokenHeaderPrefix() {
+		return "Bearer ";
+	}
+}

+ 17 - 0
src/main/resources/META-INF/additional-spring-configuration-metadata.json

@@ -0,0 +1,17 @@
+{"properties": [
+  {
+    "name": "sys.token.key",
+    "type": "java.lang.String",
+    "description": "A description for 'sys.token.key'"
+  },
+  {
+    "name": "sys.token.issuer",
+    "type": "java.lang.String",
+    "description": "A description for 'sys.token.issuer'"
+  },
+  {
+    "name": "sys.token.duration",
+    "type": "java.lang.String",
+    "description": "A description for 'sys.token.duration'"
+  }
+]}

+ 5 - 0
src/main/resources/application.properties

@@ -2,3 +2,8 @@
 spring.jpa.hibernate.ddl-auto=update
 spring.jpa.defer-datasource-initialization = true
 spring.jpa.show-sql=true
+
+# SYS
+sys.token.key=a_super_secret_passwd
+sys.token.issuer=garcosda
+sys.token.duration=120