forked from IoT-Technology/IoT-Technical-Guide
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a3bd836
commit d7f4813
Showing
10 changed files
with
474 additions
and
2 deletions.
There are no files selected for viewing
90 changes: 90 additions & 0 deletions
90
...efresh/src/main/java/iot/technology/jwt/refresh/config/CustomJwtAuthenticationFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package iot.technology.jwt.refresh.config; | ||
|
||
import io.jsonwebtoken.ExpiredJwtException; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.security.authentication.BadCredentialsException; | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.security.core.userdetails.User; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.util.StringUtils; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
|
||
import javax.servlet.FilterChain; | ||
import javax.servlet.ServletException; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
|
||
/** | ||
* @author james mu | ||
* @date 2020/9/29 15:58 | ||
*/ | ||
@Component | ||
public class CustomJwtAuthenticationFilter extends OncePerRequestFilter { | ||
|
||
@Autowired | ||
private JwtUtil jwtTokenUtil; | ||
|
||
@Override | ||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) | ||
throws ServletException, IOException { | ||
try{ | ||
// JWT Token is in the form "Bearer token". Remove Bearer word and | ||
// get only the Token | ||
String jwtToken = extractJwtFromRequest(request); | ||
|
||
if (StringUtils.hasText(jwtToken) && jwtTokenUtil.validateToken(jwtToken)) { | ||
UserDetails userDetails = new User(jwtTokenUtil.getUsernameFromToken(jwtToken), "", | ||
jwtTokenUtil.getRolesFromToken(jwtToken)); | ||
|
||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( | ||
userDetails, null, userDetails.getAuthorities()); | ||
// After setting the Authentication in the context, we specify | ||
// that the current user is authenticated. So it passes the | ||
// Spring Security Configurations successfully. | ||
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); | ||
} else { | ||
System.out.println("Cannot set the Security Context"); | ||
} | ||
}catch(ExpiredJwtException ex) | ||
{ | ||
String isRefreshToken = request.getHeader("isRefreshToken"); | ||
String requestURL = request.getRequestURL().toString(); | ||
// allow for Refresh Token creation if following conditions are true. | ||
if (isRefreshToken != null && isRefreshToken.equals("true") && requestURL.contains("refreshtoken")) { | ||
allowForRefreshToken(ex, request); | ||
} else | ||
request.setAttribute("exception", ex); | ||
} | ||
catch(BadCredentialsException ex) | ||
{ | ||
request.setAttribute("exception", ex); | ||
} | ||
chain.doFilter(request, response); | ||
} | ||
|
||
private void allowForRefreshToken(ExpiredJwtException ex, HttpServletRequest request) { | ||
|
||
// create a UsernamePasswordAuthenticationToken with null values. | ||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( | ||
null, null, null); | ||
// After setting the Authentication in the context, we specify | ||
// that the current user is authenticated. So it passes the | ||
// Spring Security Configurations successfully. | ||
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); | ||
// Set the claims so that in controller we will be using it to create | ||
// new JWT | ||
request.setAttribute("claims", ex.getClaims()); | ||
|
||
} | ||
|
||
private String extractJwtFromRequest(HttpServletRequest request) { | ||
String bearerToken = request.getHeader("Authorization"); | ||
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { | ||
return bearerToken.substring(7, bearerToken.length()); | ||
} | ||
return null; | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
...JWT-Refresh/src/main/java/iot/technology/jwt/refresh/config/CustomUserDetailsService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package iot.technology.jwt.refresh.config; | ||
|
||
import iot.technology.jwt.refresh.dao.DaoUser; | ||
import iot.technology.jwt.refresh.dao.UserDTO; | ||
import iot.technology.jwt.refresh.model.UserDao; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.security.core.authority.SimpleGrantedAuthority; | ||
import org.springframework.security.core.userdetails.User; | ||
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.Service; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Objects; | ||
|
||
/** | ||
* @author james mu | ||
* @date 2020/9/29 15:41 | ||
*/ | ||
@Service | ||
public class CustomUserDetailsService implements UserDetailsService { | ||
|
||
@Autowired | ||
private UserDao userDao; | ||
|
||
@Autowired | ||
private PasswordEncoder bcryptEncoder; | ||
|
||
@Override | ||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { | ||
List<SimpleGrantedAuthority> roles = null; | ||
DaoUser user = userDao.findByUsername(username); | ||
if (Objects.nonNull(user)) { | ||
roles = Arrays.asList(new SimpleGrantedAuthority(user.getRole())); | ||
return new User(user.getUsername(), user.getPassword(), roles); | ||
} | ||
throw new UsernameNotFoundException("User not found with the name" + username); | ||
} | ||
|
||
public DaoUser save(UserDTO user) { | ||
DaoUser newUser = new DaoUser(); | ||
newUser.setUsername(user.getUsername()); | ||
newUser.setPassword(bcryptEncoder.encode(user.getPassword())); | ||
newUser.setRole(user.getRole()); | ||
return userDao.save(newUser); | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
...-Refresh/src/main/java/iot/technology/jwt/refresh/config/JwtAuthenticationEntryPoint.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package iot.technology.jwt.refresh.config; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.web.AuthenticationEntryPoint; | ||
import org.springframework.stereotype.Component; | ||
|
||
import javax.servlet.ServletException; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
import java.util.Collections; | ||
|
||
/** | ||
* @author james mu | ||
* @date 2020/9/29 16:02 | ||
*/ | ||
@Component | ||
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { | ||
|
||
@Override | ||
public void commence(HttpServletRequest request, HttpServletResponse response, | ||
AuthenticationException authException) throws IOException, ServletException { | ||
|
||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); | ||
response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||
|
||
Exception exception = (Exception) request.getAttribute("exception"); | ||
|
||
String message; | ||
|
||
if (exception != null) { | ||
|
||
byte[] body = new ObjectMapper().writeValueAsBytes(Collections.singletonMap("cause", exception.toString())); | ||
|
||
response.getOutputStream().write(body); | ||
|
||
} else { | ||
|
||
if (authException.getCause() != null) { | ||
message = authException.getCause().toString() + " " + authException.getMessage(); | ||
} else { | ||
message = authException.getMessage(); | ||
} | ||
|
||
byte[] body = new ObjectMapper().writeValueAsBytes(Collections.singletonMap("error", message)); | ||
|
||
response.getOutputStream().write(body); | ||
} | ||
} | ||
} |
104 changes: 104 additions & 0 deletions
104
IOT-Guide-JWT-Refresh/src/main/java/iot/technology/jwt/refresh/config/JwtUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package iot.technology.jwt.refresh.config; | ||
|
||
import io.jsonwebtoken.*; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.security.authentication.BadCredentialsException; | ||
import org.springframework.security.core.GrantedAuthority; | ||
import org.springframework.security.core.authority.SimpleGrantedAuthority; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.util.*; | ||
|
||
/** | ||
* @author james mu | ||
* @date 2020/9/29 15:59 | ||
*/ | ||
@Service | ||
public class JwtUtil { | ||
|
||
private String secret; | ||
private int jwtExpirationInMs; | ||
private int refreshExpirationDateInMs; | ||
|
||
@Value("${jwt.secret}") | ||
public void setSecret(String secret) { | ||
this.secret = secret; | ||
} | ||
|
||
@Value("${jwt.expirationDateInMs}") | ||
public void setJwtExpirationInMs(int jwtExpirationInMs) { | ||
this.jwtExpirationInMs = jwtExpirationInMs; | ||
} | ||
|
||
@Value("${jwt.refreshExpirationDateInMs}") | ||
public void setRefreshExpirationDateInMs(int refreshExpirationDateInMs) { | ||
this.refreshExpirationDateInMs = refreshExpirationDateInMs; | ||
} | ||
|
||
public String generateToken(UserDetails userDetails) { | ||
Map<String, Object> claims = new HashMap<>(); | ||
|
||
Collection<? extends GrantedAuthority> roles = userDetails.getAuthorities(); | ||
|
||
if (roles.contains(new SimpleGrantedAuthority("ROLE_ADMIN"))) { | ||
claims.put("isAdmin", true); | ||
} | ||
if (roles.contains(new SimpleGrantedAuthority("ROLE_USER"))) { | ||
claims.put("isUser", true); | ||
} | ||
|
||
return doGenerateToken(claims, userDetails.getUsername()); | ||
} | ||
|
||
private String doGenerateToken(Map<String, Object> claims, String subject) { | ||
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis())) | ||
.setExpiration(new Date(System.currentTimeMillis() + jwtExpirationInMs)) | ||
.signWith(SignatureAlgorithm.HS512, secret).compact(); | ||
|
||
} | ||
|
||
public String doGenerateRefreshToken(Map<String, Object> claims, String subject) { | ||
|
||
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis())) | ||
.setExpiration(new Date(System.currentTimeMillis() + refreshExpirationDateInMs)) | ||
.signWith(SignatureAlgorithm.HS512, secret).compact(); | ||
|
||
} | ||
|
||
public boolean validateToken(String authToken) { | ||
try { | ||
Jws<Claims> claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(authToken); | ||
return true; | ||
} catch (SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException ex) { | ||
throw new BadCredentialsException("INVALID_CREDENTIALS", ex); | ||
} catch (ExpiredJwtException ex) { | ||
throw ex; | ||
} | ||
} | ||
|
||
public String getUsernameFromToken(String token) { | ||
Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); | ||
return claims.getSubject(); | ||
|
||
} | ||
|
||
public List<SimpleGrantedAuthority> getRolesFromToken(String token) { | ||
Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); | ||
|
||
List<SimpleGrantedAuthority> roles = null; | ||
|
||
Boolean isAdmin = claims.get("isAdmin", Boolean.class); | ||
Boolean isUser = claims.get("isUser", Boolean.class); | ||
|
||
if (isAdmin != null && isAdmin) { | ||
roles = Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN")); | ||
} | ||
|
||
if (isUser != null && isAdmin) { | ||
roles = Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")); | ||
} | ||
return roles; | ||
|
||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
...-Refresh/src/main/java/iot/technology/jwt/refresh/config/SpringSecurityConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package iot.technology.jwt.refresh.config; | ||
|
||
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.config.annotation.authentication.builders.AuthenticationManagerBuilder; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | ||
import org.springframework.security.config.http.SessionCreationPolicy; | ||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
|
||
/** | ||
* @author james mu | ||
* @date 2020/9/29 15:40 | ||
*/ | ||
@Configuration | ||
@EnableWebSecurity | ||
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter { | ||
|
||
@Autowired | ||
private CustomUserDetailsService userDetailsService; | ||
|
||
@Autowired | ||
private CustomJwtAuthenticationFilter customJwtAuthenticationFilter; | ||
|
||
@Autowired | ||
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; | ||
|
||
|
||
|
||
@Bean | ||
public PasswordEncoder passwordEncoder(){ | ||
return new BCryptPasswordEncoder(); | ||
} | ||
|
||
|
||
@Override | ||
public void configure(AuthenticationManagerBuilder auth) throws Exception { | ||
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); | ||
} | ||
|
||
@Bean | ||
@Override | ||
public AuthenticationManager authenticationManagerBean() throws Exception | ||
{ | ||
return super.authenticationManagerBean(); | ||
} | ||
|
||
@Override | ||
public void configure(HttpSecurity http) throws Exception { | ||
http.csrf().disable() | ||
.authorizeRequests().antMatchers("/helloadmin").hasRole("ADMIN") | ||
.antMatchers("/hellouser").hasAnyRole("USER","ADMIN") | ||
.antMatchers("/authenticate", "/register").permitAll().anyRequest().authenticated() | ||
.and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint). | ||
and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS). | ||
and().addFilterBefore(customJwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); | ||
} | ||
} |
Oops, something went wrong.