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
92caee2
commit 77e56cc
Showing
9 changed files
with
428 additions
and
0 deletions.
There are no files selected for viewing
27 changes: 27 additions & 0 deletions
27
...hout-JPA/src/main/java/iot/technology/jwt/without/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,27 @@ | ||
package iot.technology.jwt.without.config; | ||
|
||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.web.AuthenticationEntryPoint; | ||
import org.springframework.stereotype.Component; | ||
|
||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
import java.io.Serializable; | ||
|
||
/** | ||
* @author james mu | ||
* @date 2020/9/7 19:17 | ||
*/ | ||
@Component | ||
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable { | ||
|
||
private static final long serialVersionUID = -7858869558953243875L; | ||
|
||
@Override | ||
public void commence(HttpServletRequest request, HttpServletResponse response, | ||
AuthenticationException authException) throws IOException { | ||
|
||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); | ||
} | ||
} |
76 changes: 76 additions & 0 deletions
76
...ide-JWT-Without-JPA/src/main/java/iot/technology/jwt/without/config/JwtRequestFilter.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,76 @@ | ||
package iot.technology.jwt.without.config; | ||
|
||
import io.jsonwebtoken.ExpiredJwtException; | ||
import iot.technology.jwt.without.service.JwtUserDetailsService; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; | ||
import org.springframework.stereotype.Component; | ||
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/7 19:14 | ||
*/ | ||
@Slf4j | ||
@Component | ||
public class JwtRequestFilter extends OncePerRequestFilter { | ||
|
||
|
||
@Autowired | ||
private JwtUserDetailsService jwtUserDetailsService; | ||
|
||
@Autowired | ||
private JwtTokenUtil jwtTokenUtil; | ||
|
||
@Override | ||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) | ||
throws ServletException, IOException { | ||
|
||
final String requestTokenHeader = request.getHeader("Authorization"); | ||
|
||
String username = null; | ||
String jwtToken = null; | ||
// JWT Token is in the form "Bearer token". Remove Bearer word and get only the Token | ||
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) { | ||
jwtToken = requestTokenHeader.substring(7); | ||
try { | ||
username = jwtTokenUtil.getUsernameFromToken(jwtToken); | ||
} catch (IllegalArgumentException e) { | ||
System.out.println("Unable to get JWT Token"); | ||
} catch (ExpiredJwtException e) { | ||
System.out.println("JWT Token has expired"); | ||
} | ||
} else { | ||
logger.warn("JWT Token does not begin with Bearer String"); | ||
} | ||
|
||
//Once we get the token validate it. | ||
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { | ||
|
||
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username); | ||
|
||
// if token is valid configure Spring Security to manually set authentication | ||
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) { | ||
|
||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( | ||
userDetails, null, userDetails.getAuthorities()); | ||
usernamePasswordAuthenticationToken | ||
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); | ||
// 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); | ||
} | ||
} | ||
chain.doFilter(request, response); | ||
} | ||
} |
81 changes: 81 additions & 0 deletions
81
IOT-Guide-JWT-Without-JPA/src/main/java/iot/technology/jwt/without/config/JwtTokenUtil.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,81 @@ | ||
package iot.technology.jwt.without.config; | ||
|
||
import io.jsonwebtoken.Claims; | ||
import io.jsonwebtoken.Jwts; | ||
import io.jsonwebtoken.SignatureAlgorithm; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.io.Serializable; | ||
import java.util.Date; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.function.Function; | ||
|
||
/** | ||
* @author james mu | ||
* @date 2020/9/7 19:12 | ||
*/ | ||
@Component | ||
public class JwtTokenUtil implements Serializable { | ||
|
||
private static final long serialVersionUID = -2550185165626007488L; | ||
|
||
public static final long JWT_TOKEN_VALIDITY = 5*60*60; | ||
|
||
@Value("${jwt.secret}") | ||
private String secret; | ||
|
||
public String getUsernameFromToken(String token) { | ||
return getClaimFromToken(token, Claims::getSubject); | ||
} | ||
|
||
public Date getIssuedAtDateFromToken(String token) { | ||
return getClaimFromToken(token, Claims::getIssuedAt); | ||
} | ||
|
||
public Date getExpirationDateFromToken(String token) { | ||
return getClaimFromToken(token, Claims::getExpiration); | ||
} | ||
|
||
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) { | ||
final Claims claims = getAllClaimsFromToken(token); | ||
return claimsResolver.apply(claims); | ||
} | ||
|
||
private Claims getAllClaimsFromToken(String token) { | ||
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); | ||
} | ||
|
||
private Boolean isTokenExpired(String token) { | ||
final Date expiration = getExpirationDateFromToken(token); | ||
return expiration.before(new Date()); | ||
} | ||
|
||
private Boolean ignoreTokenExpiration(String token) { | ||
// here you specify tokens, for that the expiration is ignored | ||
return false; | ||
} | ||
|
||
public String generateToken(UserDetails userDetails) { | ||
Map<String, Object> claims = new HashMap<>(); | ||
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() + JWT_TOKEN_VALIDITY*1000)).signWith(SignatureAlgorithm.HS512, secret).compact(); | ||
} | ||
|
||
public Boolean canTokenBeRefreshed(String token) { | ||
return (!isTokenExpired(token) || ignoreTokenExpiration(token)); | ||
} | ||
|
||
public Boolean validateToken(String token, UserDetails userDetails) { | ||
final String username = getUsernameFromToken(token); | ||
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); | ||
} | ||
|
||
} |
71 changes: 71 additions & 0 deletions
71
...de-JWT-Without-JPA/src/main/java/iot/technology/jwt/without/config/WebSecurityConfig.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,71 @@ | ||
package iot.technology.jwt.without.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.method.configuration.EnableGlobalMethodSecurity; | ||
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.core.userdetails.UserDetailsService; | ||
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/7 19:16 | ||
*/ | ||
@Configuration | ||
@EnableWebSecurity | ||
@EnableGlobalMethodSecurity(prePostEnabled = true) | ||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { | ||
|
||
@Autowired | ||
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; | ||
|
||
@Autowired | ||
private UserDetailsService jwtUserDetailsService; | ||
|
||
@Autowired | ||
private JwtRequestFilter jwtRequestFilter; | ||
|
||
@Autowired | ||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { | ||
// configure AuthenticationManager so that it knows from where to load | ||
// user for matching credentials | ||
// Use BCryptPasswordEncoder | ||
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder()); | ||
} | ||
|
||
@Bean | ||
public PasswordEncoder passwordEncoder() { | ||
return new BCryptPasswordEncoder(); | ||
} | ||
|
||
@Bean | ||
@Override | ||
public AuthenticationManager authenticationManagerBean() throws Exception { | ||
return super.authenticationManagerBean(); | ||
} | ||
|
||
@Override | ||
protected void configure(HttpSecurity httpSecurity) throws Exception { | ||
// We don't need CSRF for this example | ||
httpSecurity.csrf().disable() | ||
// dont authenticate this particular request | ||
.authorizeRequests().antMatchers("/authenticate").permitAll(). | ||
// all other requests need to be authenticated | ||
anyRequest().authenticated().and(). | ||
// make sure we use stateless session; session won't be used to | ||
// store user's state. | ||
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement() | ||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS); | ||
|
||
// Add a filter to validate the tokens with every request | ||
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
...Without-JPA/src/main/java/iot/technology/jwt/without/controller/HelloWorldController.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,20 @@ | ||
package iot.technology.jwt.without.controller; | ||
|
||
import org.springframework.web.bind.annotation.CrossOrigin; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
/** | ||
* @author james mu | ||
* @date 2020/9/7 19:18 | ||
*/ | ||
@RestController | ||
@CrossOrigin | ||
public class HelloWorldController { | ||
|
||
@RequestMapping({ "/hello" }) | ||
public String hello() { | ||
return "Hello World"; | ||
} | ||
|
||
} |
61 changes: 61 additions & 0 deletions
61
...-JPA/src/main/java/iot/technology/jwt/without/controller/JwtAuthenticationController.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,61 @@ | ||
package iot.technology.jwt.without.controller; | ||
|
||
import iot.technology.jwt.without.config.JwtTokenUtil; | ||
import iot.technology.jwt.without.model.JwtRequest; | ||
import iot.technology.jwt.without.model.JwtResponse; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.security.authentication.AuthenticationManager; | ||
import org.springframework.security.authentication.BadCredentialsException; | ||
import org.springframework.security.authentication.DisabledException; | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
import org.springframework.security.core.userdetails.UserDetailsService; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
import java.util.Objects; | ||
|
||
/** | ||
* @author james mu | ||
* @date 2020/9/7 19:19 | ||
*/ | ||
@RestController | ||
@CrossOrigin | ||
public class JwtAuthenticationController { | ||
|
||
@Autowired | ||
private AuthenticationManager authenticationManager; | ||
|
||
@Autowired | ||
private JwtTokenUtil jwtTokenUtil; | ||
|
||
@Autowired | ||
private UserDetailsService jwtInMemoryUserDetailsService; | ||
|
||
@RequestMapping(value = "/authenticate", method = RequestMethod.POST) | ||
public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtRequest authenticationRequest) | ||
throws Exception { | ||
|
||
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword()); | ||
|
||
final UserDetails userDetails = jwtInMemoryUserDetailsService | ||
.loadUserByUsername(authenticationRequest.getUsername()); | ||
|
||
final String token = jwtTokenUtil.generateToken(userDetails); | ||
|
||
return ResponseEntity.ok(new JwtResponse(token)); | ||
} | ||
|
||
private void authenticate(String username, String password) throws Exception { | ||
Objects.requireNonNull(username); | ||
Objects.requireNonNull(password); | ||
|
||
try { | ||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); | ||
} catch (DisabledException e) { | ||
throw new Exception("USER_DISABLED", e); | ||
} catch (BadCredentialsException e) { | ||
throw new Exception("INVALID_CREDENTIALS", e); | ||
} | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
IOT-Guide-JWT-Without-JPA/src/main/java/iot/technology/jwt/without/model/JwtRequest.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,44 @@ | ||
package iot.technology.jwt.without.model; | ||
|
||
|
||
import java.io.Serializable; | ||
|
||
/** | ||
* @author james mu | ||
* @date 2020/9/7 18:30 | ||
*/ | ||
public class JwtRequest implements Serializable { | ||
|
||
|
||
private static final long serialVersionUID = 5926468583005150707L; | ||
|
||
private String username; | ||
private String password; | ||
|
||
//need default constructor for JSON Parsing | ||
public JwtRequest() | ||
{ | ||
|
||
} | ||
|
||
public JwtRequest(String username, String password) { | ||
this.setUsername(username); | ||
this.setPassword(password); | ||
} | ||
|
||
public String getUsername() { | ||
return this.username; | ||
} | ||
|
||
public void setUsername(String username) { | ||
this.username = username; | ||
} | ||
|
||
public String getPassword() { | ||
return this.password; | ||
} | ||
|
||
public void setPassword(String password) { | ||
this.password = password; | ||
} | ||
} |
Oops, something went wrong.