Implement jwt in Spring Boot & React

export const getAuthed = async (url) => {
await renewAccessTokenIfGoingExpire();

let token = getLocalToken();
let res = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + token
}
})
let j = await res.json();
return j;
}
//
export const checkAccessTokenWillExpireInDay = (days) => {
// decode access token
// check exp
// if will exp in a day
let decoded = getDecodedAccessToken();
return moment(decoded.exp * 1000).subtract(days, "days").isBefore(moment());
};

export const getDecodedAccessToken = () => {
let accessToken = getLocalToken();
let decoded = jwt_decode(accessToken);
return decoded;
}

export const exchangeRefreshTokenWithNewAccessToken = async () => {
let refreshToken = localStorage.getItem("refreshToken");
let username = getDecodedAccessToken().username;
let res = await post("/api/auth/renew", {
username,
refreshToken
});
return res.token;
};

export const renewAccessTokenIfGoingExpire = async () => {
let willExpire = checkAccessTokenWillExpireInDay(1);
if (willExpire) {
let token = await exchangeRefreshTokenWithNewAccessToken();
localStorage.setItem("token", token);
}
};
package com.stevemu.rest;

import com.fasterxml.uuid.Generators;
import com.stevemu.security.jwt.JwtTokenProvider;
import com.stevemu.user.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import static org.springframework.http.ResponseEntity.ok;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

// username: refreshToken
private HashMap<String, String> refreshTokens = new HashMap<String, String>();

@Autowired
AuthenticationManager authenticationManager;

@Autowired
JwtTokenProvider jwtTokenProvider;

@Autowired
UserRepository users;

@PostMapping("/renew")
public ResponseEntity renew(@RequestBody RefreshTokenRequest data) {

Map<Object, Object> model = new HashMap<>();

// get username
// for now, pretend it is s
String username = data.getUsername();
String refreshToken = data.getRefreshToken();

if (refreshTokens.get(refreshToken) == null) {
return new ResponseEntity(HttpStatus.UNAUTHORIZED);
}

// check if the refreshToken is for this username
if (refreshTokens.get(refreshToken).equals(username)) {
// generate a new access token for this user
String token = jwtTokenProvider.createToken(username, this.users.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("Username " + username + "not found")).getRoles());

model.put("token", token);
return ok(model);
}

return new ResponseEntity(HttpStatus.UNAUTHORIZED);



}

@PostMapping("/signin")
public ResponseEntity signin(@RequestBody AuthenticationRequest data) {

try {
String username = data.getUsername();
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, data.getPassword()));
String token = jwtTokenProvider.createToken(username, this.users.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("Username " + username + "not found")).getRoles());

// generate a refreshToken
UUID refreshToken = Generators.randomBasedGenerator().generate();

// store the refreshToken the username
refreshTokens.put(refreshToken.toString(), username);

Map<Object, Object> model = new HashMap<>();
model.put("username", username);
model.put("token", token);
model.put("refreshToken", refreshToken);

return ok(model);
} catch (AuthenticationException e) {
throw new BadCredentialsException("Invalid username/password supplied");
}
}
}
InvalidJwtAuthenticationException.java:public class InvalidJwtAuthenticationException extends AuthenticationException {
public InvalidJwtAuthenticationException(String e) {
super(e);
}
}
JwtConfigurer.java:public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

private JwtTokenProvider jwtTokenProvider;

public JwtConfigurer(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}

@Override
public void configure(HttpSecurity http) throws Exception {
JwtTokenFilter customFilter = new JwtTokenFilter(jwtTokenProvider);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
JwtTokenFilter:public class JwtTokenFilter extends GenericFilterBean {

private JwtTokenProvider jwtTokenProvider;

public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
throws IOException, ServletException {

String token = jwtTokenProvider.resolveToken((HttpServletRequest) req);
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication auth = jwtTokenProvider.getAuthentication(token);

if (auth != null) {
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
filterChain.doFilter(req, res);
}

}
JwtTokenProvider:@Component
public class JwtTokenProvider {

@Value("${security.jwt.token.secret-key:secret}")
private String secretKey = "secret";

@Value("${security.jwt.token.expire-length:172800000}")
private long validityInMilliseconds = 172800000; // 2 days

@Autowired
private UserDetailsService userDetailsService;

@PostConstruct
protected void init() {
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}

public String createToken(String username, List<String> roles) {

Claims claims = Jwts.claims().setSubject(username);
claims.put("roles", roles);
claims.put("username", username);

Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliseconds);

return Jwts.builder()//
.setClaims(claims)//
.setIssuedAt(now)//
.setExpiration(validity)//
.signWith(SignatureAlgorithm.HS256, secretKey)//
.compact();
}

public Authentication getAuthentication(String token) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(getUsername(token));
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}

public String getUsername(String token) {
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}

public String resolveToken(HttpServletRequest req) {
String bearerToken = req.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}

public boolean validateToken(String token) {
try {
Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);

if (claims.getBody().getExpiration().before(new Date())) {
return false;
}

return true;
} catch (JwtException | IllegalArgumentException e) {
throw new InvalidJwtAuthenticationException("Expired or invalid JWT token");
}
}

}
CustomUserDetailsService:@Component
public class CustomUserDetailsService implements UserDetailsService {

private UserRepository users;

public CustomUserDetailsService(UserRepository users) {
this.users = users;
}

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return this.users.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("Username: " + username + " not found"));
}
}
RefreshTokenRequest:@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RefreshTokenRequest {
private String refreshToken;
private String username;
}
AuthenticationRequest.java:@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AuthenticationRequest implements Serializable {
private String username;
private String password;
}
SecurityConfig.java:@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {


@Autowired
JwtTokenProvider jwtTokenProvider;

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

@Override
protected void configure(HttpSecurity http) throws Exception {

http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/users/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.apply(new JwtConfigurer(jwtTokenProvider));
}


}
compile group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1'
compile group: 'com.fasterxml.uuid', name: 'java-uuid-generator', version: '3.2.0'
compile group: 'org.projectlombok', name: 'lombok', version: '1.18.6'

--

--

--

Software Engineer

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

How to Copy a File to Windows Share from Linux

k8s kind

How to use GitOps with ArgoCD

How to get a 5-star rating on CodeChef quickly using YouTube for free?

SAP BW Bridge for Data Warehouse Cloud | SAP BW to SAP DWC

Which CMS to use for Website Design?

Interview Guide Series — Sliding Window

Introduction to General Purpose GPU programming

png

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Steve Mu

Steve Mu

Software Engineer

More from Medium

Push Notifications using WebSocket with Spring Boot and React(Part-1)

Spring boot

How to enable logging MongoDB queries for Spring Boot applications

Send Compressed Data From Spring Boot , Uncompress & read in ReactJS