JWT (JSON Web Token) is a JSON-based open standard (RFC 7519) implemented to pass claims between network application environments. The token is designed to be compact and secure, especially suitable for single sign-on (SSO) scenarios of distributed sites. The JWT statement is generally used to pass authenticated user identity information between identity providers and service providers to facilitate the acquisition of resources from the resource server. It can also add some additional declaration information necessary for other business logic. The token can also be used directly for authentication or encrypted.
Usually, it is very risky to expose the API directly. If you don’t talk about anything else, you can drink it if you are directly attacked by a machine. Generally speaking, a certain permission level must be divided into the API, and then a user authentication is done, and the corresponding API is given to the user based on the authentication results. At present, there are several more mainstream solutions:
OAuth is an open authorization standard that allows users to allow third-party applications to access private resources (such as photos, videos) stored by the user on a service without providing username and password to third-party applications.
OAuth allows users to provide a token instead of a username and password to access data they store in a specific service provider. Each token authorizes a specific third-party system (e.g., a video editing website) to access a specific resource (e.g., just a video in a certain album) within a specific period of time (e.g., within the next 2 hours). In this way, OAuth allows users to authorize third-party websites to access certain specific information they store in other service providers, rather than all content
The cookie authentication mechanism is to create a Session object on the server for a request for authentication, and at the same time create a Cookie object on the client's browser; the cookie object is brought to the client to match the session object on the server to achieve state management. By default, cookies will be deleted when we close the browser. However, you can modify the expire time of the cookie to make the cookie valid for a certain period of time. Authentication based on the session method will inevitably put some pressure on the server (memory storage), not easy to expand (requiring to handle distributed sessions), cross-site request forgery attacks (CSRF)
1. Compared with session, it does not need to be saved on the server and does not occupy server memory overhead.
2. Stateless and highly scalable: For example, there are 3 machines (A, B, C) to form a server cluster. If the session exists on machine A, the session can only be saved on one of the servers. At this time, you cannot access machines B and C, because the session is not stored on B and C, and using tokens can verify the legitimacy of the user's request. It's okay for me to add a few more machines, so this is what it means.
3. Front-end separation and support cross-domain access.
- The composition of JWT
{ "iss": "JWT Builder", "iat": 1416797419, "exp": 1448333419, "aud": "www.battcn.com", "sub": "[email protected]", "GivenName": "Levin", "Surname": "Levin", "Email": "[email protected]", "Role": [ "ADMIN", "MEMBER" ] }A JWT is actually a string, which consists of three parts, header, payload, and signature (sorted in sequence in the picture above)
JWT Token Generator: https://jwt.io/
- Login authentication
- Request for authentication
Invalid Token
Valid Token
There are advantages and disadvantages. Whether it is applicable should be considered clearly, rather than technology and style.
TokenProperties key mapping with application.yml resource, easy to use
@Configuration@ConfigurationProperties(prefix = "battcn.security.token")public class TokenProperties { /** * {@link com.battcn.security.model.token.Token} token expiration time*/ private Integer expirationTime; /** * Issuer*/ private String issuer; /** * Signature used KEY {@link com.battcn.security.model.token.Token}. */ private String signingKey; /** * {@link com.battcn.security.model.token.Token} Refresh expiration time*/ private Integer refreshExpTime; // get set ...} Class generated by token
@Componentpublic class TokenFactory { private final TokenProperties properties; @Autowired public TokenFactory(TokenProperties properties) { this.properties = properties; } /** * Use JJWT to generate Token * @param context * @return */ public AccessToken createAccessToken(UserContext context) { Optional.ofNullable(context.getUsername()).orElseThrow(()-> new IllegalArgumentException("Cannot create Token without username")); Optional.ofNullable(context.getAuthorities()).orElseThrow(()-> new IllegalArgumentException("User doesn't have any privileges")); Claims claims = Jwts.claims().setSubject(context.getUsername()); claims.put("scopes", context.getAuthorities().stream().map(Object::toString).collect(toList())); LocalDateTime currentTime = LocalDateTime.now(); String token = Jwts.builder() .setClaims(claims) .setIssuer(properties.getIssuer()) .setIssuedAt(Date.from(currentTime.atZone(ZoneId.systemDefault()).toInstant())) .setExpiration(Date.from(currentTime.plusMinutes(properties.getExpirationTime()) .atZone(ZoneId.systemDefault()).toInstant())) .signWith(SignatureAlgorithm.HS512, properties.getSigningKey()) .compact(); return new AccessToken(token, claims); } /** * Generate and refresh RefreshToken * @param userContext * @return */ public Token createRefreshToken(UserContext userContext) { if (StringUtils.isBlank(userContext.getUsername()))) { throw new IllegalArgumentException("Cannot create Token without username"); } LocalDateTime currentTime = LocalDateTime.now(); Claims claims = Jwts.claims().setSubject(userContext.getUsername()); claims.put("scopes", Arrays.asList(Scopes.REFRESH_TOKEN.authority())); String token = Jwts.builder() .setClaims(claims) .setIssuer(properties.getIssuer()) .setId(UUID.randomUUID().toString()) .setIssuedAt(Date.from(currentTime.atZone(ZoneId.systemDefault()).toInstant())) .setExpiration(Date.from(currentTime .plusMinutes(properties.getRefreshExpTime())) .atZone(ZoneId.systemDefault()).toInstant())) .signWith(SignatureAlgorithm.HS512, properties.getSigningKey()) .compact(); return new AccessToken(token, claims); }} Configuration file, including token expiration time, secret key, can be expanded by itself
battcn: security: token: expiration-time: 10 # minutes 1440 refresh-exp-time: 30 # minutes 2880 issuer: http://blog.battcn.com signing-key: battcn
WebSecurityConfig is a key configuration of Spring Security. In Security, we can basically implement the functions we want by defining filters.
@Configuration@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { public static final String TOKEN_HEADER_PARAM = "X-Authorization"; public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login"; public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**"; public static final String MANAGE_TOKEN_BASED_AUTH_ENTRY_POINT = "/manage/**"; public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token"; @Autowired private RestAuthenticationEntryPoint authenticationEntryPoint; @Autowired private AuthenticationSuccessHandler successHandler; @Autowired private AuthenticationFailureHandler failureHandler; @Autowired private LoginAuthenticationProvider loginAuthenticationProvider; @Autowired private TokenAuthenticationProvider tokenAuthenticationProvider; @Autowired private TokenExtractor tokenExtractor; @Autowired private AuthenticationManager authenticationManager; protected LoginProcessingFilter buildLoginProcessingFilter() throws Exception { LoginProcessingFilter filter = new LoginProcessingFilter(FORM_BASED_LOGIN_ENTRY_POINT, successHandler, failureHandler); filter.setAuthenticationManager(this.authenticationManager); return filter; } protected TokenAuthenticationProcessingFilter buildTokenAuthenticationProcessingFilter() throws Exception { List<String> list = Lists.newArrayList(TOKEN_BASED_AUTH_ENTRY_POINT,MANAGE_TOKEN_BASED_AUTH_ENTRY_POINT); SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(list); TokenAuthenticationProcessingFilter filter = new TokenAuthenticationProcessingFilter(failureHandler, tokenExtractor, matcher); filter.setAuthenticationManager(this.authenticationManager); return filter; } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(AuthenticationManagerBuilder auth) { auth.authenticationProvider(loginAuthenticationProvider); auth.authenticationProvider(tokenAuthenticationProvider); } @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() // Because it is using JWT, you can turn off csrf here.exceptionHandling() .authenticationEntryPoint(this.authenticationEntryPoint) .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll() // Login end-point .antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point .and() .authorizeRequests() .antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated() // Protected API End-points .antMatchers(MANAGE_TOKEN_BASED_AUTH_ENTRY_POINT).hasAnyRole(RoleEnum.ADMIN.name()) .and() .addFilterBefore(buildLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(buildTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class); }}Since the JWT code is simply encapsulated and contains a lot of content, only the main fragments are posted in the article. If the complete code is needed, you can directly obtain it from the following GIT
Code of this chapter (battcn-jwt-service): http://xiazai.VeVB.COM/201801/yuanma/battcn-cloud_jb51.rar
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.