赞
踩
一、获取授权码Code:
访问授权服务器 /oauth/authorize 端点:(只用于"implicit", "authorization_code")
GET: http://127.0.0.1:8080/oauth/authorize?client_id=client&response_type=code&redirect_uri=http://www.baidu.com
参数:
(1)response_type=code 必须指定。
(2) client_id=client 客户端必须指定。
(3)redirect_uri=http:// 重定向地址 必须与数据表一致
结果:如果同意授权: 返回code=cown42 并跳转到redirect_uri=http://www.baidu.com
过程:比较client_id、及redirect_uri、scope、
涉及到核心类:
(1)DefaultRedirectResolver:redirect_uri地址校验。
(2)JdbcAuthorizationCodeServices/InMemoryAuthorizationCodeServices: 生成的code,并保存(code,OAuth2Authentication)在内存或数据库(oauth_code表)
(3)JdbcClientDetailsService: 从表oauth_client_details读取ClientDetails信息,用来校验。
(4)LoginUrlAuthenticationEntryPoint:末登录时,异常由ExceptionTranslationFilter doFilter()---》LoginUrlAuthenticationEntryPoint--》重定向到loginFromUrl页面。
- public class AuthorizationEndpoint extends AbstractEndpoint {
-
- //一、相关操作:
- private AuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
- private RedirectResolver redirectResolver = new DefaultRedirectResolver();
- private UserApprovalHandler userApprovalHandler = new DefaultUserApprovalHandler();
- private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
- private OAuth2RequestValidator oauth2RequestValidator = new DefaultOAuth2RequestValidator();
- //授权确认页面
- private String userApprovalPage = "forward:/oauth/confirm_access";
- private String errorPage = "forward:/oauth/error";
-
- //二、接口:
- @RequestMapping(value = "/oauth/authorize")
- public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<String, String> parameters,SessionStatus sessionStatus, Principal principal) {
- /*1. 通过DefaultOAuth2RequestFactory.createAuthorizationRequest()
- AuthorizationRequest request = new AuthorizationRequest();
- 从client数据库表加载clietDetails信息
- ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
- request.setResourceIdsAndAuthoritiesFromClientDetails(clientDetails);
- */
- AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters);
-
- Set<String> responseTypes = authorizationRequest.getResponseTypes();
- //2.反应类型必须是code
- if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
- throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
- }
- //3.必须指定clientId
- if (authorizationRequest.getClientId() == null) {
- throw new InvalidClientException("A client id must be provided");
- }
-
- try {
- //4.访问该端点必须认证isAuthenticated = ture,抛出异常,由ExceptionTranslationFilter doFilter()---》LoginUrlAuthenticationEntryPoint--》重定向到loginFromUrl页面。
- if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
- throw new InsufficientAuthenticationException(
- "User must be authenticated with Spring Security before authorization can be completed.");
- }
- //5.从数据库加载clientDetails
- ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());
-
- //6. 必须指定redirect_uri 而且与数据库里的设置一至。同时认证类型只能:"implicit", "authorization_code"
- String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
- String resolvedRedirect = redirectResolver.resolveRedirect(redirectUriParameter, client);
- if (!StringUtils.hasText(resolvedRedirect)) {
- throw new RedirectMismatchException(
- "A redirectUri must be either supplied or preconfigured in the ClientDetails");
- }
- authorizationRequest.setRedirectUri(resolvedRedirect);
-
- // 7。访问范畴Scope比对
- oauth2RequestValidator.validateScope(authorizationRequest, client);
-
- // 8.是否受权
- authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest,
- (Authentication) principal);
- // TODO: is this call necessary?
- boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
- authorizationRequest.setApproved(approved);
-
- // 10.授权通过后,直接返回数据。
- // (1) Code授权:生成code,同时保存在InMemoryAuthorizationCodeServices(默认)或JdbcAuthorizationCodeServices(表oauth_code)
- if (authorizationRequest.isApproved()) {
- if (responseTypes.contains("token")) {
- return getImplicitGrantResponse(authorizationRequest);
- }
- if (responseTypes.contains("code")) {
- return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest,
- (Authentication) principal));
- }
- }
-
- //11.弹窗口,让用户授权
- model.put("authorizationRequest", authorizationRequest);
-
- return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
-
- }
- catch (RuntimeException e) {
- sessionStatus.setComplete();
- throw e;
- }
-
- }
二、 获取访问令牌: 访问授权服务器 /oauth/token 端点
确保用户是已经登陆的情况,返回access_token格式.
- Post:
- http://localhost:8080/oauth/token?
- grant_type=authorization_code&code=o4YrCS&client_id=pair &client_secret=secret&redirect_uri=http://baidu.com
参数:
(1) clientId: 客户端id,参数指定同时Principal也必须一致。否则 InvalidClientException(必须)
(2)client_secret:客户端密匙
(2)grant_type:授权类型。(必须)
(3)code: 授权码(必须)
核心类:
(1)AuthorizationServerTokenServices/DefaultTokenServices:由这类产生、刷新、获取token.
(2) TokenStore:由用户自己提供实现代码。
(3)类包装关系: AbstractEndpoint----》AuthorizationServerTokenServices(DefaultTokenServices)---》TokenStore
- @FrameworkEndpoint
- public class TokenEndpoint extends AbstractEndpoint {
-
- private OAuth2RequestValidator oAuth2RequestValidator = new DefaultOAuth2RequestValidator();
-
- /**
- * 获取access_token
- **/
- @RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
- public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
- Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
- //1.必须是认证通过的用户,否则会抛出异常,异常由ExceptionTranslationFilter doFilter()---》LoginUrlAuthenticationEntryPoint--》重定向到loginFromUrl页面。
- if (!(principal instanceof Authentication)) {
- throw new InsufficientAuthenticationException(
- "There is no client authentication. Try adding an appropriate authentication filter.");
- }
- //2.读取数据表加载ClientDetails信息。
- String clientId = getClientId(principal);
- ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
-
- TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
- //2.检查clientId是否正确
- if (clientId != null && !clientId.equals("")) {
- if (!clientId.equals(tokenRequest.getClientId())) {
- throw new InvalidClientException("Given client ID does not match authenticated client");
- }
- }
- //3.检查Scope
- if (authenticatedClient != null) {
- oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
- }
- //4.检查GrantType: 且不能为implicit
- if (!StringUtils.hasText(tokenRequest.getGrantType())) {
- throw new InvalidRequestException("Missing grant type");
- }
- if (tokenRequest.getGrantType().equals("implicit")) {
- throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
- }
- //5.如果是authorization_code类型,检查AuthCode:AuthCode != null && grant_type = authorization_code
- if (isAuthCodeRequest(parameters)) {
- // The scope was requested or determined during the authorization step
- if (!tokenRequest.getScope().isEmpty()) {
- logger.debug("Clearing scope of incoming token request");
- tokenRequest.setScope(Collections.<String> emptySet());
- }
- }
- // 授权是刷新token: grant_type = refresh_token
- if (isRefreshTokenRequest(parameters)) {
- // A refresh token has its own default scopes, so we should ignore any added by the factory here.
- tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
- }
- //5.生成 token,TokenGranter(用户指定)生成一个OAuth2AccessToken。
-
- OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
- if (token == null) {
- throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
- }
-
- return getResponse(token);
-
- }
- public abstract class AbstractTokenGranter implements TokenGranter {
-
- /**
- * 核心类:提供token所有相关操作(最终由DefaultTokenServices实现类实现功能)
- **/
- private final AuthorizationServerTokenServices tokenServices;
- private final ClientDetailsService clientDetailsService;
- private final OAuth2RequestFactory requestFactory;
- private final String grantType;
- }
- OAuth2AccessToken {
- public static String BEARER_TYPE = "Bearer";
- public static String OAUTH2_TYPE = "OAuth2";
- public static String ACCESS_TOKEN = "access_token";
- public static String TOKEN_TYPE = "token_type";
- public static String EXPIRES_IN = "expires_in";
- public static String REFRESH_TOKEN = "refresh_token";
- public static String SCOPE = "scope";
- }
- /**
- * 1.TokenStore:核心类,所有token相关的操作由它提供
- * 2.核心功能:(1)提供accessToken 的创建、刷新服务 (2)loadAuthentication()accessToken获取用户信息过程
- * 3.核心类:
- * (1)AuthorizationServerTokenServices:授权服务器的提供accessToken 的创建、刷新服务。
- * (2)ResourceServerTokenServices:授权服务器的提供accessToken 获取用户信息,提供给资源服务器去tokenString---> OAuth2Authentication
- */
- public class DefaultTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices,
- ConsumerTokenServices, InitializingBean {
-
- //Token生成、保存服务。(核心重点)
- private TokenStore tokenStore;
- //ClientDetail 服务
- private ClientDetailsService clientDetailsService;
- //Token信息添加
- private TokenEnhancer accessTokenEnhancer;
- //认证管理:当token刷新时,用于重新认证
- private AuthenticationManager authenticationManager;
-
- /**
- * 生成 OAuth2AccessToken
- */
- @Transactional
- public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
-
- //1. 直接从tokenStore服务获取accessToken
- OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
- OAuth2RefreshToken refreshToken = null;
- //1.tokenStore有accessToken时,检查是否失效。
- if (existingAccessToken != null) {
- if (existingAccessToken.isExpired()) {
- if (existingAccessToken.getRefreshToken() != null) {
- refreshToken = existingAccessToken.getRefreshToken();
- tokenStore.removeRefreshToken(refreshToken);
- }
- tokenStore.removeAccessToken(existingAccessToken);
- }
- else {
- tokenStore.storeAccessToken(existingAccessToken, authentication);
- return existingAccessToken;
- }
- }
-
- //2.生成 accessToken 和 refreshToken 并保存
- refreshToken = createRefreshToken(authentication);
- OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
- tokenStore.storeAccessToken(accessToken, authentication);
- tokenStore.storeRefreshToken(refreshToken, authentication);
-
- return accessToken;
-
- }
-
- /**
- * 刷新aceessToken
- */
- @Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class})
- public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
- throws AuthenticationException {
- //1. 刷新token
- OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue);
- //2.重新认证
- OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken);
- //3.重新生成、保存token
- refreshToken = createRefreshToken(authentication);
- OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
- tokenStore.storeAccessToken(accessToken, authentication);
- tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication);
-
- return accessToken;
- }
-
- /**
- * 从accessToken 获取到OAuth2Authentication
- */
- public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException,
- InvalidTokenException {
- OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue);
- OAuth2Authentication result = tokenStore.readAuthentication(accessToken);
- //校验
- if (clientDetailsService != null) {
- String clientId = result.getOAuth2Request().getClientId();
- try {
- clientDetailsService.loadClientByClientId(clientId);
- }
- catch (ClientRegistrationException e) {
- throw new InvalidTokenException("Client not valid: " + clientId, e);
- }
- }
- return result;
- }
-
- /**
- * 生成RefreshToken就是简单的json字符串
- */
- private OAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) {
- // OAuth2RefreshToken数据结构: @JsonValue String getValue();
- return new DefaultOAuth2RefreshToken(value);
- }
-
- /**
- * 生成 OAuth2AccessToken
- */
- private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
- //生成AccessToken
- /**OAuth2AccessToken数据结构
- private String value;
- private Date expiration;
- private String tokenType = BEARER_TYPE.toLowerCase();
- private OAuth2RefreshToken refreshToken;
- private Set<String> scope;
- private Map<String, Object> additionalInformation = Collections.emptyMap();
- */
-
- DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
- token.setRefreshToken(refreshToken);
- token.setScope(authentication.getOAuth2Request().getScope());
- //调用用户指定的accessTokenEnhancer进行数据插入
- return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
- }
-
-
- }
三、访问资源服务器受保护的资源:
根据access_token获取资源, 附上令牌在请求头,**需加上 Bearer **
访问http://localhost:8080/rest/api/ping?access_token=a8ae6a78-289d-4594-a421-9b56aa8f7213
(1)PostMan工具: GET/POST: /xxx/xxxx Header: Authorization Bearer access_token
(2)Curl命令: curl -X GET \ http://localhost:9001/test \ -i -H "Accept: application/json" -H "Authorization: Bearer eyJhbG" \
四、配置AuthorizationServerEndpointsConfigurer:
主要注入想着的ServerBean。
- /**
- * 配置token的保存方式
- * 1. 密码模式下配置认证管理器 AuthenticationManager
- * 2. 设置 AccessToken的存储介质tokenStore, 默认使用内存当做存储介质。
- */
- @Override
- public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
-
- TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
- endpoints
- .authenticationManager(authenticationManager)
- .userDetailsService(userDetailsService) //MUST:密码模式下需设置一个AuthenticationManager对象,获取 UserDetails信息
- .tokenStore(tokenStore)//token的保存方式
- .tokenEnhancer(tokenEnhancerChain);//token里加点信息
- }
-
- //所有Endpoints所用的到服务都在这里注入完成,保存在这个对象
- public final class AuthorizationServerEndpointsConfigurer {
-
- private AuthorizationServerTokenServices tokenServices;
-
- private ConsumerTokenServices consumerTokenServices;
-
- private AuthorizationCodeServices authorizationCodeServices;
-
- private ResourceServerTokenServices resourceTokenServices;
-
- private TokenStore tokenStore;
-
- private TokenEnhancer tokenEnhancer;
-
- private AccessTokenConverter accessTokenConverter;
-
- private ApprovalStore approvalStore;
-
- private TokenGranter tokenGranter;
-
- private OAuth2RequestFactory requestFactory;
-
- private OAuth2RequestValidator requestValidator;
-
- private UserApprovalHandler userApprovalHandler;
-
- private AuthenticationManager authenticationManager;
-
- private ClientDetailsService clientDetailsService;
-
- private String prefix;
-
- private Map<String, String> patternMap = new HashMap<String, String>();
-
- private Set<HttpMethod> allowedTokenEndpointRequestMethods = new HashSet<HttpMethod>();
-
- private FrameworkEndpointHandlerMapping frameworkEndpointHandlerMapping;
-
- private boolean approvalStoreDisabled;
-
- private List<Object> interceptors = new ArrayList<Object>();
-
- private DefaultTokenServices defaultTokenServices;
-
- private UserDetailsService userDetailsService;
-
- private boolean tokenServicesOverride = false;
-
- private boolean userDetailsServiceOverride = false;
-
- private boolean reuseRefreshToken = true;
-
- private WebResponseExceptionTranslator<OAuth2Exception> exceptionTranslator;
-
- private RedirectResolver redirectResolver;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。