Spring Security Oauth2使用JWT生成Token
阿新 • • 發佈:2018-11-15
轉載務必說明出處:https://blog.csdn.net/LiaoHongHB/article/details/84031281
在我們平時使用oauth2的協議中,生成的token是基於oauth2協議本身的生成策略,如下面的程式碼(一般在登陸成功的handler中生成token).:
package com.car.springsecurity.handle; import com.car.springsecurity.base.RestFulVO; import com.car.springsecurity.dto.UserToken; import com.car.springsecurity.entity.User; import com.car.springsecurity.utils.JwtUtils; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.TokenRequest; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.stereotype.Component; import org.apache.commons.collections.MapUtils; import javax.annotation.Resource; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Base64; /** * @author liaohong * @since 2018/10/29 10:56 */ @Component("myAuthenticationSuccessHandler") public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { private Logger logger = LoggerFactory.getLogger(MyAuthenticationSuccessHandler.class); @Autowired private ObjectMapper objectMapper; @Autowired private ClientDetailsService clientDetailsService; @Autowired private AuthorizationServerTokenServices authorizationServerTokenServices; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { // token鑑權 String header = request.getHeader("Authorization"); if (header == null || !header.startsWith("Basic ")) { throw new UnapprovedClientAuthenticationException("請求頭中無client資訊"); } String[] tokens = extractAndDecodeHeader(header, request); assert tokens.length == 2; String clientId = tokens[0]; String clientSecret = tokens[1]; ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId); if (clientDetails == null) { throw new UnapprovedClientAuthenticationException("clientId對應的配置資訊不存在" + clientId); } if (!StringUtils.equals(clientDetails.getClientSecret(), clientSecret)) { throw new UnapprovedClientAuthenticationException("clientSecret不匹配" + clientId); } TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP, clientId, clientDetails.getScope(), ""); OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails); OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication); OAuth2AccessToken oAuth2AccessToken = authorizationServerTokenServices.createAccessToken(oAuth2Authentication); if (StringUtils.isNotEmpty(oAuth2AccessToken.getValue())) { logger.info("{}登入成功", authentication.getName()); String tokenValue = oAuth2AccessToken.getValue(); logger.info("tokenValue------>{}", tokenValue); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(tokenValue)); } } private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException { byte[] base64Token = header.substring(6).getBytes("UTF-8"); byte[] decoded; try { decoded = Base64.getDecoder().decode(base64Token); } catch (IllegalArgumentException e) { throw new BadCredentialsException( "Failed to decode basic authentication token"); } String token = new String(decoded, "UTF-8"); int delim = token.indexOf(":"); if (delim == -1) { throw new BadCredentialsException("Invalid basic authentication token"); } return new String[]{token.substring(0, delim), token.substring(delim + 1)}; } }
使用postman軟體我們可以進行測試:
可以看到console列印的log日誌資訊是:
這是基於oauth2協議本身生成的token,然而一般在開發過程中,我們往往採用的是jwt生成token;因為
1、jwt安全性比較高,加密演算法眾多而且加上簽名可以發現token被改寫
2、jwt可以自定義屬性資訊
3、jwt儲存在客戶端,不用存放在記憶體,減少了伺服器的壓力
那麼spring security oauth2生成jwt token的具體操作如下:
改寫認證伺服器,繼承AuthorizationServerConfigurerAdapter,重寫其中的幾個方法:
package com.car.springsecurity.config; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; /** * Created by hong.liao on 2018/11/13. */ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Bean public OAuth2WebSecurityExpressionHandler oAuth2WebSecurityExpressionHandler(ApplicationContext applicationContext) { OAuth2WebSecurityExpressionHandler expressionHandler = new OAuth2WebSecurityExpressionHandler(); expressionHandler.setApplicationContext(applicationContext); return expressionHandler; } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("client") .secret("clientsecret") //支援的授權模式(陣列型別) .authorizedGrantTypes("authorization_code", "refresh_token","password") .scopes("all"); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter()); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.tokenKeyAccess("isAuthenticated()"); } @Bean public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); //JWT簽名 converter.setSigningKey("0123456789"); return converter; } }
application.yml檔案中的配置:
server:
port: 5001
security:
oauth2:
client:
client-id: client
client-secret: clientsecret
user-authorization-uri: http://127.0.0.1:5001/oauth/authorize
access-token-uri: http://127.0.0.1:5001/oauth/token
resource:
jwt:
key-uri: http://127.0.0.1:5001/oauth/token_key
SuccessHandle中程式碼不動。
使用postman進行測試:
console列印log日誌如下:
開啟JSON WEB Tokens網站對生成的token進行解析:
解析token:
至此,spring security oauth2生成jwt token就說明完成。