Skip to content

Commit

Permalink
Use Spring security oauth2ResourceServer and spring oauth2-resource-s…
Browse files Browse the repository at this point in the history
…erver for security
  • Loading branch information
nilshartmann committed Dec 28, 2022
1 parent 286c1b4 commit 944de84
Show file tree
Hide file tree
Showing 15 changed files with 280 additions and 278 deletions.
35 changes: 7 additions & 28 deletions backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version>
<version>3.0.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.springframework.samples</groupId>
Expand Down Expand Up @@ -37,6 +37,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
Expand Down Expand Up @@ -86,33 +90,8 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jdbc-core</artifactId>
<version>1.2.1.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
<version>0.11.2</version>
<scope>runtime</scope>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package org.springframework.samples.petclinic.graphql;

import graphql.schema.DataFetchingEnvironment;
import graphql.schema.idl.RuntimeWiring;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
import org.springframework.samples.petclinic.auth.User;
import org.springframework.security.core.Authentication;
import org.springframework.samples.petclinic.auth.UserRepository;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.stereotype.Controller;

import java.security.Principal;

/**
* EXAMPLE:
* --------------------------
Expand All @@ -25,9 +24,17 @@
public class AuthController {
private static final Logger log = LoggerFactory.getLogger(AuthController.class);

private final UserRepository userRepository;

public AuthController(UserRepository userRepository) {
this.userRepository = userRepository;
}

@QueryMapping
public User me(@AuthenticationPrincipal User user) {
return user;
public User me(@AuthenticationPrincipal(errorOnInvalidType = true) Jwt jwt) {
String username = jwt.getSubject();
log.info("JWT subject (username): '{}'", username);
return userRepository.findByUsername(username).orElseThrow();
}

@QueryMapping
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
* GraphQL handler functions for Vet GraphQL type, Query and Mutation
*
* Note that the addVet mutation is secured in the domain layer, so that only
* users with ROLE_MANAGER are allowed to create new vets
* users with SCOPE_MANAGER are allowed to create new vets
*
* @author Nils Hartmann ([email protected])
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public VetService(VetRepository vetRepository, SpecialtyRepository specialtyRepo
}

@Transactional
@PreAuthorize("hasRole('ROLE_MANAGER')")
@PreAuthorize("hasAuthority('SCOPE_MANAGER')")
public Vet createVet(String firstName, String lastName, List<Integer> specialtyIds) throws InvalidVetDataException {
Vet vet = new Vet();
vet.setFirstName(firstName);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,97 +1,49 @@
package org.springframework.samples.petclinic.security;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.samples.petclinic.auth.User;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
import org.springframework.stereotype.Service;

import jakarta.annotation.PostConstruct;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.stream.Collectors;

/**
* Note that this is an example only. DO NOT IMPLEMENT OWN SECURITY CODE IN REAL PRODUCTION APPS !!!!!!!!!!
*
* @author Nils Hartmann ([email protected])
* Based con code taken from Dan Vega https://github.com/danvega/jwt-username-password/blob/master/src/main/java/dev/danvega/jwt/service/TokenService.java
*/
@Service
public class JwtTokenService {
private final Logger logger = LoggerFactory.getLogger(getClass());

@Value("${jwt.expirationInMs:7200000}")
private int jwtExpirationInMs;
private final JwtEncoder encoder;

private final SecretKey secretKey;

public JwtTokenService(@Value("${jwt.secretString:fasdfahsdufak4923674asbclbca73,f,a,dfw}") String secretString) {
this.secretKey = Keys.hmacShaKeyFor(secretString.getBytes(StandardCharsets.UTF_8));
}

/** Creates a token that will never expire and will be stable accross re-starts
* as longs as jwt.secretString does not change.
*
* This token can be used for easier testing using command line tools etc.
* YOU SHOULD NEVER DO THIS IN 'REAL' PRODUCTION APPS
*/
@PostConstruct
void createNeverExpiringToken() throws Exception {
SimpleDateFormat f = new SimpleDateFormat("dd.MM.yyyy HH:mm");
String neverExpiringManagerToken = Jwts.builder()
.setSubject("susi")
.setIssuedAt(f.parse("25.12.2020 10:44"))
.setExpiration(f.parse("25.12.2044 10:44"))
.signWith(secretKey)
.compact();

String neverExpiringUserToken = Jwts.builder()
.setSubject("joe")
.setIssuedAt(f.parse("25.12.2020 10:44"))
.setExpiration(f.parse("25.12.2044 10:44"))
.signWith(secretKey)
.compact();
logger.info("\n\nNever Expiring JWT Token\n\n - ROLE_MANAGER: '{}'\n As HTTP Header: 'Authorization: Bearer {}'\n\n - ROLE_USER: '{}'\n As HTTP Header: 'Authorization: Bearer {}'\n", neverExpiringManagerToken, neverExpiringManagerToken, neverExpiringUserToken, neverExpiringUserToken);
public JwtTokenService(JwtEncoder encoder) {
this.encoder = encoder;
}

public String createTokenForUser(User user) {

Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpirationInMs);

return Jwts.builder()
.setSubject(user.getUsername())
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(secretKey)
.compact();
public String generateToken(Authentication authentication) {
return generateToken(authentication.getName(),
authentication.getAuthorities(),
Instant.now().plus(1, ChronoUnit.HOURS)
);
}

public String getUsernameFromToken(String token) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody();

return claims.getSubject();
}
public String generateToken(String name, Collection<? extends GrantedAuthority> authorities, Instant expiresAt) {
String scope = authorities.stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(" "));

public boolean isValidToken(String authToken) {
try {
Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(authToken);
return true;
} catch (Exception ex) {
logger.info("Invalid JWT token: " + ex);
}
JwtClaimsSet claims = JwtClaimsSet.builder()
.issuer("self")
.issuedAt(Instant.now())
.expiresAt(expiresAt)
.subject(name)
.claim("scope", scope)
.build();

return false;
return this.encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
}
}
Loading

0 comments on commit 944de84

Please sign in to comment.