Skip to content

Commit 78d6448

Browse files
author
Nelson
committed
8 - JWT
1 parent 3dae146 commit 78d6448

File tree

8 files changed

+291
-25
lines changed

8 files changed

+291
-25
lines changed

pom.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,26 @@
4040
<version>28.1-jre</version>
4141
</dependency>
4242

43+
<dependency>
44+
<groupId>io.jsonwebtoken</groupId>
45+
<artifactId>jjwt-api</artifactId>
46+
<version>0.10.7</version>
47+
</dependency>
48+
49+
<dependency>
50+
<groupId>io.jsonwebtoken</groupId>
51+
<artifactId>jjwt-impl</artifactId>
52+
<version>0.10.7</version>
53+
<scope>runtime</scope>
54+
</dependency>
55+
56+
<dependency>
57+
<groupId>io.jsonwebtoken</groupId>
58+
<artifactId>jjwt-jackson</artifactId>
59+
<version>0.10.7</version>
60+
<scope>runtime</scope>
61+
</dependency>
62+
4363
<dependency>
4464
<groupId>org.springframework.boot</groupId>
4565
<artifactId>spring-boot-starter-test</artifactId>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.example.demo.jwt;
2+
3+
import com.google.common.net.HttpHeaders;
4+
import io.jsonwebtoken.security.Keys;
5+
import org.springframework.boot.context.properties.ConfigurationProperties;
6+
import org.springframework.context.annotation.Bean;
7+
8+
import javax.crypto.SecretKey;
9+
10+
@ConfigurationProperties(prefix = "application.jwt")
11+
public class JwtConfig {
12+
13+
private String secretKey;
14+
private String tokenPrefix;
15+
private Integer tokenExpirationAfterDays;
16+
17+
public JwtConfig() {
18+
}
19+
20+
public String getSecretKey() {
21+
return secretKey;
22+
}
23+
24+
public void setSecretKey(String secretKey) {
25+
this.secretKey = secretKey;
26+
}
27+
28+
public String getTokenPrefix() {
29+
return tokenPrefix;
30+
}
31+
32+
public void setTokenPrefix(String tokenPrefix) {
33+
this.tokenPrefix = tokenPrefix;
34+
}
35+
36+
public Integer getTokenExpirationAfterDays() {
37+
return tokenExpirationAfterDays;
38+
}
39+
40+
public void setTokenExpirationAfterDays(Integer tokenExpirationAfterDays) {
41+
this.tokenExpirationAfterDays = tokenExpirationAfterDays;
42+
}
43+
44+
public String getAuthorizationHeader() {
45+
return HttpHeaders.AUTHORIZATION;
46+
}
47+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.example.demo.jwt;
2+
3+
import io.jsonwebtoken.security.Keys;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.Configuration;
7+
8+
import javax.crypto.SecretKey;
9+
10+
@Configuration
11+
public class JwtSecretKey {
12+
13+
private final JwtConfig jwtConfig;
14+
15+
@Autowired
16+
public JwtSecretKey(JwtConfig jwtConfig) {
17+
this.jwtConfig = jwtConfig;
18+
}
19+
20+
@Bean
21+
public SecretKey secretKey() {
22+
return Keys.hmacShaKeyFor(jwtConfig.getSecretKey().getBytes());
23+
}
24+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.example.demo.jwt;
2+
3+
import com.google.common.base.Strings;
4+
import io.jsonwebtoken.Claims;
5+
import io.jsonwebtoken.Jws;
6+
import io.jsonwebtoken.JwtException;
7+
import io.jsonwebtoken.Jwts;
8+
import io.jsonwebtoken.security.Keys;
9+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
10+
import org.springframework.security.core.Authentication;
11+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
12+
import org.springframework.security.core.context.SecurityContextHolder;
13+
import org.springframework.web.filter.OncePerRequestFilter;
14+
15+
import javax.crypto.SecretKey;
16+
import javax.servlet.FilterChain;
17+
import javax.servlet.ServletException;
18+
import javax.servlet.http.HttpServletRequest;
19+
import javax.servlet.http.HttpServletResponse;
20+
import java.io.IOException;
21+
import java.util.List;
22+
import java.util.Map;
23+
import java.util.Set;
24+
import java.util.stream.Collectors;
25+
26+
public class JwtTokenVerifier extends OncePerRequestFilter {
27+
28+
private final SecretKey secretKey;
29+
private final JwtConfig jwtConfig;
30+
31+
public JwtTokenVerifier(SecretKey secretKey,
32+
JwtConfig jwtConfig) {
33+
this.secretKey = secretKey;
34+
this.jwtConfig = jwtConfig;
35+
}
36+
37+
@Override
38+
protected void doFilterInternal(HttpServletRequest request,
39+
HttpServletResponse response,
40+
FilterChain filterChain) throws ServletException, IOException {
41+
42+
String authorizationHeader = request.getHeader(jwtConfig.getAuthorizationHeader());
43+
44+
if (Strings.isNullOrEmpty(authorizationHeader) || !authorizationHeader.startsWith(jwtConfig.getTokenPrefix())) {
45+
filterChain.doFilter(request, response);
46+
return;
47+
}
48+
49+
String token = authorizationHeader.replace(jwtConfig.getTokenPrefix(), "");
50+
51+
try {
52+
53+
Jws<Claims> claimsJws = Jwts.parser()
54+
.setSigningKey(secretKey)
55+
.parseClaimsJws(token);
56+
57+
Claims body = claimsJws.getBody();
58+
59+
String username = body.getSubject();
60+
61+
var authorities = (List<Map<String, String>>) body.get("authorities");
62+
63+
Set<SimpleGrantedAuthority> simpleGrantedAuthorities = authorities.stream()
64+
.map(m -> new SimpleGrantedAuthority(m.get("authority")))
65+
.collect(Collectors.toSet());
66+
67+
Authentication authentication = new UsernamePasswordAuthenticationToken(
68+
username,
69+
null,
70+
simpleGrantedAuthorities
71+
);
72+
73+
SecurityContextHolder.getContext().setAuthentication(authentication);
74+
75+
} catch (JwtException e) {
76+
throw new IllegalStateException(String.format("Token %s cannot be trusted", token));
77+
}
78+
79+
filterChain.doFilter(request, response);
80+
}
81+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.example.demo.jwt;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import io.jsonwebtoken.Jwts;
5+
import io.jsonwebtoken.security.Keys;
6+
import org.springframework.security.authentication.AuthenticationManager;
7+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
8+
import org.springframework.security.core.Authentication;
9+
import org.springframework.security.core.AuthenticationException;
10+
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
11+
12+
import javax.crypto.SecretKey;
13+
import javax.servlet.FilterChain;
14+
import javax.servlet.ServletException;
15+
import javax.servlet.http.HttpServletRequest;
16+
import javax.servlet.http.HttpServletResponse;
17+
import java.io.IOException;
18+
import java.time.LocalDate;
19+
import java.util.Date;
20+
21+
public class JwtUsernameAndPasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
22+
23+
private final AuthenticationManager authenticationManager;
24+
private final JwtConfig jwtConfig;
25+
private final SecretKey secretKey;
26+
27+
public JwtUsernameAndPasswordAuthenticationFilter(AuthenticationManager authenticationManager,
28+
JwtConfig jwtConfig,
29+
SecretKey secretKey) {
30+
this.authenticationManager = authenticationManager;
31+
this.jwtConfig = jwtConfig;
32+
this.secretKey = secretKey;
33+
}
34+
35+
@Override
36+
public Authentication attemptAuthentication(HttpServletRequest request,
37+
HttpServletResponse response) throws AuthenticationException {
38+
39+
try {
40+
UsernameAndPasswordAuthenticationRequest authenticationRequest = new ObjectMapper()
41+
.readValue(request.getInputStream(), UsernameAndPasswordAuthenticationRequest.class);
42+
43+
Authentication authentication = new UsernamePasswordAuthenticationToken(
44+
authenticationRequest.getUsername(),
45+
authenticationRequest.getPassword()
46+
);
47+
48+
Authentication authenticate = authenticationManager.authenticate(authentication);
49+
return authenticate;
50+
51+
} catch (IOException e) {
52+
throw new RuntimeException(e);
53+
}
54+
55+
}
56+
57+
@Override
58+
protected void successfulAuthentication(HttpServletRequest request,
59+
HttpServletResponse response,
60+
FilterChain chain,
61+
Authentication authResult) throws IOException, ServletException {
62+
String token = Jwts.builder()
63+
.setSubject(authResult.getName())
64+
.claim("authorities", authResult.getAuthorities())
65+
.setIssuedAt(new Date())
66+
.setExpiration(java.sql.Date.valueOf(LocalDate.now().plusDays(jwtConfig.getTokenExpirationAfterDays())))
67+
.signWith(secretKey)
68+
.compact();
69+
70+
response.addHeader(jwtConfig.getAuthorizationHeader(), jwtConfig.getTokenPrefix() + token);
71+
}
72+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.example.demo.jwt;
2+
3+
public class UsernameAndPasswordAuthenticationRequest {
4+
5+
private String username;
6+
private String password;
7+
8+
public UsernameAndPasswordAuthenticationRequest() {
9+
}
10+
11+
public String getUsername() {
12+
return username;
13+
}
14+
15+
public void setUsername(String username) {
16+
this.username = username;
17+
}
18+
19+
public String getPassword() {
20+
return password;
21+
}
22+
23+
public void setPassword(String password) {
24+
this.password = password;
25+
}
26+
}

src/main/java/com/example/demo/security/ApplicationSecurityConfig.java

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package com.example.demo.security;
22

33
import com.example.demo.auth.ApplicationUserService;
4+
import com.example.demo.jwt.JwtConfig;
5+
import com.example.demo.jwt.JwtTokenVerifier;
6+
import com.example.demo.jwt.JwtUsernameAndPasswordAuthenticationFilter;
47
import org.springframework.beans.factory.annotation.Autowired;
58
import org.springframework.context.annotation.Bean;
69
import org.springframework.context.annotation.Configuration;
@@ -10,10 +13,10 @@
1013
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
1114
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
1215
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
16+
import org.springframework.security.config.http.SessionCreationPolicy;
1317
import org.springframework.security.crypto.password.PasswordEncoder;
14-
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
1518

16-
import java.util.concurrent.TimeUnit;
19+
import javax.crypto.SecretKey;
1720

1821
import static com.example.demo.security.ApplicationUserRole.*;
1922

@@ -25,43 +28,34 @@ public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
2528

2629
private final PasswordEncoder passwordEncoder;
2730
private final ApplicationUserService applicationUserService;
31+
private final SecretKey secretKey;
32+
private final JwtConfig jwtConfig;
2833

2934
@Autowired
3035
public ApplicationSecurityConfig(PasswordEncoder passwordEncoder,
31-
ApplicationUserService applicationUserService) {
36+
ApplicationUserService applicationUserService,
37+
SecretKey secretKey,
38+
JwtConfig jwtConfig) {
3239
this.passwordEncoder = passwordEncoder;
3340
this.applicationUserService = applicationUserService;
41+
this.secretKey = secretKey;
42+
this.jwtConfig = jwtConfig;
3443
}
3544

3645
@Override
3746
protected void configure(HttpSecurity http) throws Exception {
3847
http
3948
.csrf().disable()
49+
.sessionManagement()
50+
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
51+
.and()
52+
.addFilter(new JwtUsernameAndPasswordAuthenticationFilter(authenticationManager(), jwtConfig, secretKey))
53+
.addFilterAfter(new JwtTokenVerifier(secretKey, jwtConfig),JwtUsernameAndPasswordAuthenticationFilter.class)
4054
.authorizeRequests()
4155
.antMatchers("/", "index", "/css/*", "/js/*").permitAll()
4256
.antMatchers("/api/**").hasRole(STUDENT.name())
4357
.anyRequest()
44-
.authenticated()
45-
.and()
46-
.formLogin()
47-
.loginPage("/login")
48-
.permitAll()
49-
.defaultSuccessUrl("/courses", true)
50-
.passwordParameter("password")
51-
.usernameParameter("username")
52-
.and()
53-
.rememberMe()
54-
.tokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(21))
55-
.key("somethingverysecured")
56-
.rememberMeParameter("remember-me")
57-
.and()
58-
.logout()
59-
.logoutUrl("/logout")
60-
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET")) // https://docs.spring.io/spring-security/site/docs/4.2.12.RELEASE/apidocs/org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.html
61-
.clearAuthentication(true)
62-
.invalidateHttpSession(true)
63-
.deleteCookies("JSESSIONID", "remember-me")
64-
.logoutSuccessUrl("/login");
58+
.authenticated();
6559
}
6660

6761
@Override
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
1+
application.jwt.secretKey=securesecuresecuresecuresecuresecuresecuresecuresecuresecuresecure
2+
application.jwt.tokenPrefix=Bearer
3+
application.jwt.tokenExpirationAfterDays=10

0 commit comments

Comments
 (0)