protocl fix

This commit is contained in:
shimingxy
2019-06-02 08:43:34 +08:00
parent 86701ba7d0
commit e62f96fa73
169 changed files with 2856 additions and 7134 deletions

View File

@@ -0,0 +1,86 @@
package org.maxkey.authz.oauth2.provider.approval.controller;
import java.security.Principal;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import org.maxkey.authz.oauth2.common.util.OAuth2Utils;
import org.maxkey.authz.oauth2.provider.AuthorizationRequest;
import org.maxkey.authz.oauth2.provider.ClientDetailsService;
import org.maxkey.authz.oauth2.provider.approval.Approval;
import org.maxkey.authz.oauth2.provider.approval.ApprovalStore;
import org.maxkey.authz.oauth2.provider.approval.Approval.ApprovalStatus;
import org.maxkey.domain.apps.oauth2.provider.ClientDetails;
import org.maxkey.web.WebContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;
/**
* Controller for retrieving the model for and displaying the confirmation page
* for access to a protected resource.
*
* @author Ryan Heaton
*/
@Controller
@SessionAttributes("authorizationRequest")
public class OAuth20AccessConfirmationController {
@Autowired
@Qualifier("oauth20JdbcClientDetailsService")
private ClientDetailsService clientDetailsService;
@Autowired
@Qualifier("oauth20ApprovalStore")
private ApprovalStore approvalStore;
@Autowired
@Qualifier("oauth20UserApprovalHandler")
OAuth20UserApprovalHandler oauth20UserApprovalHandler;
@RequestMapping("/oauth/v20/approval_confirm")
public ModelAndView getAccessConfirmation(@RequestParam Map<String, Object> model) throws Exception {
model.remove("authorizationRequest");
Map<String, String> modelRequest=new HashMap<String, String>();
for(Object key:model.keySet()){
modelRequest.put(key.toString(), model.get(key).toString());
}
Principal principal=(Principal)WebContext.getAuthentication().getPrincipal();
//Map<String, Object> model
AuthorizationRequest clientAuth = (AuthorizationRequest) WebContext.getAttribute("authorizationRequest");
ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
model.put("auth_request", clientAuth);
model.put("client", client);
model.put("oauth_version", "oauth 2.0");
Map<String, String> scopes = new LinkedHashMap<String, String>();
for (String scope : clientAuth.getScope()) {
scopes.put(OAuth2Utils.SCOPE_PREFIX + scope, "false");
}
for (Approval approval : approvalStore.getApprovals(principal.getName(), client.getClientId())) {
if (clientAuth.getScope().contains(approval.getScope())) {
scopes.put(OAuth2Utils.SCOPE_PREFIX + approval.getScope(),
approval.getStatus() == ApprovalStatus.APPROVED ? "true" : "false");
}
}
model.put("scopes", scopes);
ModelAndView modelAndView=new ModelAndView("authorize/oauth_access_confirmation");
modelAndView.addObject("model",model);
return modelAndView;
}
@RequestMapping("/oauth/v20/error")
public String handleError(Map<String,Object> model) throws Exception {
// We can add more stuff to the model here for JSP rendering. If the client was a machine then
// the JSON will already have been rendered.
model.put("message", "There was a problem with the OAuth2 protocol");
return "oauth_error";
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.maxkey.authz.oauth2.provider.approval.controller;
import java.util.Collection;
import org.maxkey.authz.oauth2.provider.AuthorizationRequest;
import org.maxkey.authz.oauth2.provider.ClientDetailsService;
import org.maxkey.authz.oauth2.provider.ClientRegistrationException;
import org.maxkey.authz.oauth2.provider.approval.ApprovalStoreUserApprovalHandler;
import org.maxkey.domain.apps.oauth2.provider.ClientDetails;
import org.springframework.security.core.Authentication;
/**
* @author Dave Syer
*
*/
public class OAuth20UserApprovalHandler extends ApprovalStoreUserApprovalHandler {
private boolean useApprovalStore = true;
private ClientDetailsService clientDetailsService;
/**
* Service to load client details (optional) for auto approval checks.
*
* @param clientDetailsService a client details service
*/
public void setClientDetailsService(ClientDetailsService clientDetailsService) {
this.clientDetailsService = clientDetailsService;
super.setClientDetailsService(clientDetailsService);
}
/**
* @param useApprovalStore the useTokenServices to set
*/
public void setUseApprovalStore(boolean useApprovalStore) {
this.useApprovalStore = useApprovalStore;
}
/**
* Allows automatic approval for a white list of clients in the implicit grant case.
*
* @param authorizationRequest The authorization request.
* @param userAuthentication the current user authentication
*
* @return An updated request if it has already been approved by the current user.
*/
@Override
public AuthorizationRequest checkForPreApproval(AuthorizationRequest authorizationRequest,
Authentication userAuthentication) {
boolean approved = false;
// If we are allowed to check existing approvals this will short circuit the decision
if (useApprovalStore) {
authorizationRequest = super.checkForPreApproval(authorizationRequest, userAuthentication);
approved = authorizationRequest.isApproved();
}
else {
if (clientDetailsService != null) {
Collection<String> requestedScopes = authorizationRequest.getScope();
try {
ClientDetails client = clientDetailsService
.loadClientByClientId(authorizationRequest.getClientId());
for (String scope : requestedScopes) {
if (client.isAutoApprove(scope) || client.isAutoApprove("all")) {
approved = true;
break;
}
}
}
catch (ClientRegistrationException e) {
}
}
}
authorizationRequest.setApproved(approved);
return authorizationRequest;
}
}

View File

@@ -16,13 +16,26 @@
package org.maxkey.authz.oauth2.provider.endpoint;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.maxkey.authz.oauth2.provider.ClientDetailsService;
import org.maxkey.authz.oauth2.provider.CompositeTokenGranter;
import org.maxkey.authz.oauth2.provider.OAuth2RequestFactory;
import org.maxkey.authz.oauth2.provider.TokenGranter;
import org.maxkey.authz.oauth2.provider.client.ClientCredentialsTokenGranter;
import org.maxkey.authz.oauth2.provider.code.AuthorizationCodeServices;
import org.maxkey.authz.oauth2.provider.code.AuthorizationCodeTokenGranter;
import org.maxkey.authz.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.maxkey.authz.oauth2.provider.implicit.ImplicitTokenGranter;
import org.maxkey.authz.oauth2.provider.refresh.RefreshTokenGranter;
import org.maxkey.authz.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.maxkey.authz.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.util.Assert;
/**
@@ -34,14 +47,46 @@ public class AbstractEndpoint implements InitializingBean {
protected final Log logger = LogFactory.getLog(getClass());
private TokenGranter tokenGranter;
@Autowired
@Qualifier("oauth20AuthorizationCodeServices")
protected AuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
@Autowired
@Qualifier("oauth20TokenServices")
AuthorizationServerTokenServices tokenServices ;
@Autowired
@Qualifier("oauth20JdbcClientDetailsService")
private ClientDetailsService clientDetailsService;
@Autowired
@Qualifier("oAuth2RequestFactory")
private OAuth2RequestFactory oAuth2RequestFactory;
@Autowired
@Qualifier("oAuth2RequestFactory")
private OAuth2RequestFactory defaultOAuth2RequestFactory;
public void afterPropertiesSet() throws Exception {
if (tokenGranter == null) {
//ClientDetailsService clientDetails = clientDetailsService();
//AuthorizationServerTokenServices tokenServices = tokenServices();
//AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices();
//OAuth2RequestFactory requestFactory = requestFactory();
List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();
tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices,
clientDetailsService, oAuth2RequestFactory));
tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetailsService, oAuth2RequestFactory));
ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, clientDetailsService, oAuth2RequestFactory);
tokenGranters.add(implicit);
tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetailsService, oAuth2RequestFactory));
/*if (authenticationManager != null) {
tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices,
clientDetails, requestFactory));
}*/
tokenGranter = new CompositeTokenGranter(tokenGranters);
}
Assert.state(tokenGranter != null, "TokenGranter must be provided");
Assert.state(clientDetailsService != null, "ClientDetailsService must be provided");
defaultOAuth2RequestFactory = new DefaultOAuth2RequestFactory(getClientDetailsService());

View File

@@ -42,6 +42,7 @@ import org.maxkey.authz.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.maxkey.authz.oauth2.provider.implicit.ImplicitTokenRequest;
import org.maxkey.authz.oauth2.provider.request.DefaultOAuth2RequestValidator;
import org.maxkey.domain.apps.oauth2.provider.ClientDetails;
import org.maxkey.web.WebContext;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
@@ -82,7 +83,7 @@ import org.springframework.web.util.UriTemplate;
@SessionAttributes("authorizationRequest")
public class AuthorizationEndpoint extends AbstractEndpoint {
private AuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
private RedirectResolver redirectResolver = new DefaultRedirectResolver();
@@ -92,7 +93,7 @@ public class AuthorizationEndpoint extends AbstractEndpoint {
private OAuth2RequestValidator oauth2RequestValidator = new DefaultOAuth2RequestValidator();
private String userApprovalPage = "forward:/oauth/v20/confirm_access";
private String userApprovalPage = "forward:/oauth/v20/approval_confirm";
private String errorPage = "forward:/oauth/error";
@@ -108,8 +109,8 @@ public class AuthorizationEndpoint extends AbstractEndpoint {
@RequestMapping(value = "/oauth/v20/authorize", method = RequestMethod.GET)
public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<String, String> parameters,
SessionStatus sessionStatus, Principal principal) {
SessionStatus sessionStatus) {
Principal principal=(Principal)WebContext.getAuthentication().getPrincipal();
// Pull out the authorization request first, using the OAuth2RequestFactory. All further logic should
// query off of the authorization request instead of referring back to the parameters map. The contents of the
// parameters map will be stored without change in the AuthorizationRequest object once it is created.
@@ -187,8 +188,8 @@ public class AuthorizationEndpoint extends AbstractEndpoint {
@RequestMapping(value = "/oauth/v20/authorize", method = RequestMethod.POST, params = OAuth2Utils.USER_OAUTH_APPROVAL)
public View approveOrDeny(@RequestParam Map<String, String> approvalParameters, Map<String, ?> model,
SessionStatus sessionStatus, Principal principal) {
SessionStatus sessionStatus) {
Principal principal=(Principal)WebContext.getAuthentication().getPrincipal();
if (!(principal instanceof Authentication)) {
sessionStatus.setComplete();
throw new InsufficientAuthenticationException(

View File

@@ -35,6 +35,7 @@ import org.maxkey.authz.oauth2.provider.TokenRequest;
import org.maxkey.authz.oauth2.provider.request.DefaultOAuth2RequestValidator;
import org.maxkey.domain.apps.oauth2.provider.ClientDetails;
import org.maxkey.util.StringGenerator;
import org.maxkey.web.WebContext;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
@@ -81,18 +82,21 @@ public class TokenEndpoint extends AbstractEndpoint {
* @throws HttpRequestMethodNotSupportedException
*/
@RequestMapping(value = "/oauth/v20/token", method=RequestMethod.GET)
public ResponseEntity<OAuth2AccessToken> getAccessToken(Principal principal, @RequestParam
public ResponseEntity<OAuth2AccessToken> getAccessToken(@RequestParam
Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
if (!allowedRequestMethods.contains(HttpMethod.GET)) {
throw new HttpRequestMethodNotSupportedException("GET");
}
return postAccessToken(principal, parameters);
return postAccessToken(parameters);
}
@RequestMapping(value = "/oauth/v20/token", method=RequestMethod.POST)
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
public ResponseEntity<OAuth2AccessToken> postAccessToken(@RequestParam
Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
// TokenEndpointAuthenticationFilter
Principal principal=(Principal)WebContext.getAuthentication().getPrincipal();
if (!(principal instanceof Authentication)) {
throw new InsufficientAuthenticationException(
"There is no client authentication. Try adding an appropriate authentication filter.");
@@ -161,7 +165,7 @@ public class TokenEndpoint extends AbstractEndpoint {
if (!client.isAuthenticated()) {
throw new InsufficientAuthenticationException("The client is not authenticated.");
}
String clientId = client.getName();
String clientId = client.getPrincipal().toString();
if (client instanceof OAuth2Authentication) {
// Might be a client and user combined authentication
clientId = ((OAuth2Authentication) client).getOAuth2Request().getClientId();

View File

@@ -32,11 +32,13 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.maxkey.authn.BasicAuthentication;
import org.maxkey.authz.oauth2.common.util.OAuth2Utils;
import org.maxkey.authz.oauth2.provider.AuthorizationRequest;
import org.maxkey.authz.oauth2.provider.OAuth2Authentication;
import org.maxkey.authz.oauth2.provider.OAuth2Request;
import org.maxkey.authz.oauth2.provider.OAuth2RequestFactory;
import org.maxkey.web.WebContext;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
@@ -45,9 +47,11 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.HttpRequestMethodNotSupportedException;
/**
* <p>
@@ -74,11 +78,16 @@ public class TokenEndpointAuthenticationFilter implements Filter {
private static final Log logger = LogFactory.getLog(TokenEndpointAuthenticationFilter.class);
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
private final AuthenticationManager authenticationManager;
boolean allowOnlyPost;
private final OAuth2RequestFactory oAuth2RequestFactory;
private AuthenticationManager authenticationManager;
private OAuth2RequestFactory oAuth2RequestFactory;
public TokenEndpointAuthenticationFilter() {
}
/**
* @param authenticationManager an AuthenticationManager for the incoming request
@@ -102,49 +111,29 @@ public class TokenEndpointAuthenticationFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
ServletException {
logger.debug("Authentication TokenEndpoint ");
if(authenticationManager==null) {
authenticationManager=(AuthenticationManager)WebContext.getBean("oauth20ClientAuthenticationManager");
}
if(oAuth2RequestFactory==null) {
oAuth2RequestFactory=(OAuth2RequestFactory)WebContext.getBean("oAuth2RequestFactory");
}
final boolean debug = logger.isDebugEnabled();
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
try {
Authentication credentials = extractCredentials(request);
if (credentials != null) {
if (debug) {
logger.debug("Authentication credentials found for '" + credentials.getName() + "'");
}
Authentication authResult = authenticationManager.authenticate(credentials);
if (debug) {
logger.debug("Authentication success: " + authResult.getName());
}
Authentication clientAuth = SecurityContextHolder.getContext().getAuthentication();
if (clientAuth == null) {
throw new BadCredentialsException(
"No client authentication found. Remember to put a filter upstream of the TokenEndpointAuthenticationFilter.");
}
Map<String, String> map = getSingleValueMap(request);
map.put(OAuth2Utils.CLIENT_ID, clientAuth.getName());
AuthorizationRequest authorizationRequest = oAuth2RequestFactory.createAuthorizationRequest(map);
authorizationRequest.setScope(getScope(request));
if (clientAuth.isAuthenticated()) {
// Ensure the OAuth2Authentication is authenticated
authorizationRequest.setApproved(true);
}
OAuth2Request storedOAuth2Request = oAuth2RequestFactory.createOAuth2Request(authorizationRequest);
SecurityContextHolder.getContext().setAuthentication(
new OAuth2Authentication(storedOAuth2Request, authResult));
onSuccessfulAuthentication(request, response, authResult);
String grantType = request.getParameter("grant_type");
if (grantType != null && grantType.equals("password")) {
usernamepassword(request,response);
}else {
Authentication authentication=ClientCredentials(request,response);
BasicAuthentication auth =new BasicAuthentication();
auth.setJ_username(((User)authentication.getPrincipal()).getUsername());
auth.setAuthenticated(true);
UsernamePasswordAuthenticationToken simpleUserAuthentication = new UsernamePasswordAuthenticationToken(auth, authentication.getCredentials(), authentication.getAuthorities());
WebContext.setAuthentication(simpleUserAuthentication);
}
}
@@ -163,6 +152,86 @@ public class TokenEndpointAuthenticationFilter implements Filter {
chain.doFilter(request, response);
}
public void usernamepassword(HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException {
logger.debug("Authentication TokenEndpoint ");
try {
Authentication credentials = extractCredentials(request);
if (credentials != null) {
logger.debug("Authentication credentials found for '" + credentials.getName() + "'");
Authentication authResult = authenticationManager.authenticate(credentials);
logger.debug("Authentication success: " + authResult.getName());
Authentication clientAuth = SecurityContextHolder.getContext().getAuthentication();
if (clientAuth == null) {
throw new BadCredentialsException(
"No client authentication found. Remember to put a filter upstream of the TokenEndpointAuthenticationFilter.");
}
Map<String, String> map = getSingleValueMap(request);
map.put(OAuth2Utils.CLIENT_ID, clientAuth.getName());
AuthorizationRequest authorizationRequest = oAuth2RequestFactory.createAuthorizationRequest(map);
authorizationRequest.setScope(getScope(request));
if (clientAuth.isAuthenticated()) {
// Ensure the OAuth2Authentication is authenticated
authorizationRequest.setApproved(true);
}
OAuth2Request storedOAuth2Request = oAuth2RequestFactory.createOAuth2Request(authorizationRequest);
WebContext.setAuthentication(new OAuth2Authentication(storedOAuth2Request, authResult));
onSuccessfulAuthentication(request, response, authResult);
}
}
catch (AuthenticationException failed) {
SecurityContextHolder.clearContext();
logger.debug("Authentication request for failed: " + failed);
onUnsuccessfulAuthentication(request, response, failed);
return;
}
}
public Authentication ClientCredentials(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
if (allowOnlyPost && !"POST".equalsIgnoreCase(request.getMethod())) {
throw new HttpRequestMethodNotSupportedException(request.getMethod(), new String[] { "POST" });
}
String clientId = request.getParameter("client_id");
String clientSecret = request.getParameter("client_secret");
// If the request is already authenticated we can assume that this
// filter is not needed
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
return authentication;
}
if (clientId == null) {
throw new BadCredentialsException("No client credentials presented");
}
if (clientSecret == null) {
clientSecret = "";
}
clientId = clientId.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(clientId,clientSecret);
return this.authenticationManager.authenticate(authRequest);
}
private Map<String, String> getSingleValueMap(HttpServletRequest request) {
Map<String, String> map = new HashMap<String, String>();
Map<String, String[]> parameters = request.getParameterMap();
@@ -209,5 +278,42 @@ public class TokenEndpointAuthenticationFilter implements Filter {
public void destroy() {
}
protected static class ClientCredentialsRequestMatcher implements RequestMatcher {
private String path;
public ClientCredentialsRequestMatcher(String path) {
this.path = path;
}
@Override
public boolean matches(HttpServletRequest request) {
String uri = request.getRequestURI();
int pathParamIndex = uri.indexOf(';');
if (pathParamIndex > 0) {
// strip everything after the first semi-colon
uri = uri.substring(0, pathParamIndex);
}
String clientId = request.getParameter("client_id");
if (clientId == null) {
// Give basic auth a chance to work instead (it's preferred anyway)
return false;
}
if ("".equals(request.getContextPath())) {
return uri.endsWith(path);
}
return uri.endsWith(request.getContextPath() + path);
}
}
}

View File

@@ -0,0 +1,48 @@
package org.maxkey.authz.oauth2.provider.userinfo.endpoint;
import java.util.HashMap;
import org.maxkey.authz.endpoint.adapter.AbstractAuthorizeAdapter;
import org.maxkey.domain.UserInfo;
import org.maxkey.util.DateUtils;
import org.maxkey.util.JsonUtils;
import org.maxkey.util.StringGenerator;
import org.springframework.web.servlet.ModelAndView;
public class OAuthDefaultUserInfoAdapter extends AbstractAuthorizeAdapter {
@Override
public String generateInfo(UserInfo userInfo,Object app) {
HashMap<String, Object> beanMap = new HashMap<String, Object>();
beanMap.put("randomId",(new StringGenerator()).uuidGenerate());
beanMap.put("uid", userInfo.getId());
beanMap.put("username", userInfo.getUsername());
beanMap.put("employeeNumber", userInfo.getEmployeeNumber());
beanMap.put("email", userInfo.getEmail());
beanMap.put("mobile", userInfo.getMobile());
beanMap.put("realname", userInfo.getDisplayName());
beanMap.put("birthday", userInfo.getBirthDate());
beanMap.put("department", userInfo.getDepartment());
beanMap.put("createdate", userInfo.getCreatedDate());
beanMap.put("title", userInfo.getJobTitle());
beanMap.put("state", userInfo.getWorkRegion());
beanMap.put("gender", userInfo.getGender());
String info= JsonUtils.object2Json(beanMap);
return info;
}
@Override
public String encrypt(String data, String algorithmKey, String algorithm) {
// TODO Auto-generated method stub
return null;
}
@Override
public ModelAndView authorize(UserInfo userInfo, Object app, String data,ModelAndView modelAndView) {
// TODO Auto-generated method stub
return null;
}
}

View File

@@ -0,0 +1,312 @@
package org.maxkey.authz.oauth2.provider.userinfo.endpoint;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Set;
import java.util.UUID;
import org.maxkey.authz.endpoint.adapter.AbstractAuthorizeAdapter;
import org.maxkey.authz.oauth2.common.exceptions.OAuth2Exception;
import org.maxkey.authz.oauth2.provider.ClientDetailsService;
import org.maxkey.authz.oauth2.provider.OAuth2Authentication;
import org.maxkey.authz.oauth2.provider.token.DefaultTokenServices;
import org.maxkey.constants.BOOLEAN;
import org.maxkey.crypto.ReciprocalUtils;
import org.maxkey.crypto.jwt.encryption.service.JwtEncryptionAndDecryptionService;
import org.maxkey.crypto.jwt.encryption.service.impl.RecipientJwtEncryptionAndDecryptionServiceBuilder;
import org.maxkey.crypto.jwt.signer.service.JwtSigningAndValidationService;
import org.maxkey.crypto.jwt.signer.service.impl.SymmetricSigningAndValidationServiceBuilder;
import org.maxkey.dao.service.ApplicationsService;
import org.maxkey.dao.service.UserInfoService;
import org.maxkey.domain.UserInfo;
import org.maxkey.domain.apps.Applications;
import org.maxkey.domain.apps.oauth2.provider.ClientDetails;
import org.maxkey.util.Instance;
import org.maxkey.util.JsonUtils;
import org.maxkey.util.StringGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.nimbusds.jose.EncryptionMethod;
import com.nimbusds.jose.JWEAlgorithm;
import com.nimbusds.jose.JWEHeader;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jwt.EncryptedJWT;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
@Controller
@RequestMapping(value = { "/api" })
public class UserInfoEndpoint {
final static Logger _logger = LoggerFactory.getLogger(UserInfoEndpoint.class);
@Autowired
@Qualifier("oauth20JdbcClientDetailsService")
private ClientDetailsService clientDetailsService;
@Autowired
@Qualifier("oauth20TokenServices")
private DefaultTokenServices oauth20tokenServices;
@Autowired
@Qualifier("userInfoService")
private UserInfoService userInfoService;
@Autowired
@Qualifier("applicationsService")
protected ApplicationsService applicationsService;
@Autowired
@Qualifier("jwtSignerValidationService")
private JwtSigningAndValidationService jwtSignerValidationService;
@Autowired
@Qualifier("jwtEncryptionService")
private JwtEncryptionAndDecryptionService jwtEnDecryptionService;
private SymmetricSigningAndValidationServiceBuilder symmetricJwtSignerServiceBuilder
=new SymmetricSigningAndValidationServiceBuilder();
private RecipientJwtEncryptionAndDecryptionServiceBuilder recipientJwtEnDecryptionServiceBuilder
=new RecipientJwtEncryptionAndDecryptionServiceBuilder();
OAuthDefaultUserInfoAdapter defaultOAuthUserInfoAdapter=new OAuthDefaultUserInfoAdapter();
@RequestMapping(value="/oauth/v20/me",produces="text/plain;charset=UTF-8")
@ResponseBody
public String apiV20UserInfo(
@RequestParam(value = "access_token", required = true) String access_token) {
String principal="";
if (!StringGenerator.uuidMatches(access_token)) {
return accessTokenFormatError(access_token);
}
OAuth2Authentication oAuth2Authentication =null;
try{
oAuth2Authentication = oauth20tokenServices.loadAuthentication(access_token);
principal=oAuth2Authentication.getPrincipal().toString();
String client_id= oAuth2Authentication.getOAuth2Request().getClientId();
UserInfo userInfo=queryUserInfo(principal);
Applications app=applicationsService.get(client_id);
String userJson="";
AbstractAuthorizeAdapter adapter;
if(BOOLEAN.isTrue(app.getIsAdapter())){
adapter =(AbstractAuthorizeAdapter)Instance.newInstance(app.getAdapter());
}else{
adapter =(AbstractAuthorizeAdapter)defaultOAuthUserInfoAdapter;
}
String jsonData=adapter.generateInfo(userInfo, null);
userJson=adapter.sign(jsonData, app);
return userJson;
}catch(OAuth2Exception e){
HashMap<String,Object>authzException=new HashMap<String,Object>();
authzException.put(OAuth2Exception.ERROR, e.getOAuth2ErrorCode());
authzException.put(OAuth2Exception.DESCRIPTION,e.getMessage());
return JsonUtils.object2Json(authzException);
}
}
@RequestMapping(value="/connect/v10/userinfo",produces="text/plain;charset=UTF-8")
@ResponseBody
public String apiConnect10aUserInfo(
@RequestHeader(value = "Authorization", required = true) String access_token) {
String principal="";
if (!StringGenerator.uuidMatches(access_token)) {
return accessTokenFormatError(access_token);
}
OAuth2Authentication oAuth2Authentication =null;
try{
oAuth2Authentication = oauth20tokenServices.loadAuthentication(access_token);
principal=oAuth2Authentication.getPrincipal().toString();
Set<String >scopes=oAuth2Authentication.getOAuth2Request().getScope();
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(oAuth2Authentication.getOAuth2Request().getClientId());
UserInfo userInfo=queryUserInfo(principal);
String userJson="";
HashMap<String, Object> claimsFields = new HashMap<String, Object>();
claimsFields.put("sub", userInfo.getId());
if(scopes.contains("profile")){
claimsFields.put("name", userInfo.getUsername());
claimsFields.put("preferred_username", userInfo.getDisplayName());
claimsFields.put("given_name", userInfo.getGivenName());
claimsFields.put("family_name", userInfo.getFamilyName());
claimsFields.put("middle_name", userInfo.getMiddleName());
claimsFields.put("nickname", userInfo.getNickName());
claimsFields.put("profile", "profile");
claimsFields.put("picture", "picture");
claimsFields.put("website", userInfo.getWebSite());
String gender;
switch(userInfo.getGender()){
case UserInfo.GENDER.MALE :
gender="male";break;
case UserInfo.GENDER.FEMALE :
gender="female";break;
default:
gender="unknown";
}
claimsFields.put("gender", gender);
claimsFields.put("zoneinfo", userInfo.getTimeZone());
claimsFields.put("locale", userInfo.getLocale());
claimsFields.put("updated_time", userInfo.getModifiedDate());
claimsFields.put("birthdate", userInfo.getBirthDate());
}
if(scopes.contains("email")){
claimsFields.put("email", userInfo.getWorkEmail());
claimsFields.put("email_verified", false);
}
if(scopes.contains("phone")){
claimsFields.put("phone_number", userInfo.getWorkPhoneNumber());
claimsFields.put("phone_number_verified", false);
}
if(scopes.contains("address")){
HashMap<String, String> addressFields = new HashMap<String, String>();
addressFields.put("country", userInfo.getWorkCountry());
addressFields.put("region", userInfo.getWorkRegion());
addressFields.put("locality", userInfo.getWorkLocality());
addressFields.put("street_address", userInfo.getWorkStreetAddress());
addressFields.put("formatted", userInfo.getWorkAddressFormatted());
addressFields.put("postal_code", userInfo.getWorkPostalCode());
claimsFields.put("address", addressFields);
}
JWTClaimsSet userInfoJWTClaims = new JWTClaimsSet.Builder()
.jwtID(UUID.randomUUID().toString())// set a random NONCE in the middle of it
.audience(Arrays.asList(clientDetails.getClientId()))
.issueTime(new Date())
.expirationTime(new Date(new Date().getTime()+clientDetails.getAccessTokenValiditySeconds()*1000))
.claim(claimsFields)
.build();
JWT userInfoJWT=null;
JWSAlgorithm signingAlg = jwtSignerValidationService.getDefaultSigningAlgorithm();
if (clientDetails.getUserInfoEncryptedAlgorithm() != null && !clientDetails.getUserInfoEncryptedAlgorithm().equals("none")
&& clientDetails.getUserInfoEncryptionMethod() != null && !clientDetails.getUserInfoEncryptionMethod().equals("none")
&&clientDetails.getJwksUri()!=null&&clientDetails.getJwksUri().length()>4
) {
JwtEncryptionAndDecryptionService recipientJwtEnDecryptionService =
recipientJwtEnDecryptionServiceBuilder.serviceBuilder(clientDetails.getJwksUri());
if (recipientJwtEnDecryptionService != null) {
JWEAlgorithm jweAlgorithm=new JWEAlgorithm(clientDetails.getUserInfoEncryptedAlgorithm());
EncryptionMethod encryptionMethod=new EncryptionMethod(clientDetails.getUserInfoEncryptionMethod());
EncryptedJWT encryptedJWT = new EncryptedJWT(new JWEHeader(jweAlgorithm, encryptionMethod), userInfoJWTClaims);
recipientJwtEnDecryptionService.encryptJwt(encryptedJWT);
userJson=encryptedJWT.serialize();
}else{
_logger.error("Couldn't find encrypter for client: " + clientDetails.getClientId());
HashMap<String,Object>authzException=new HashMap<String,Object>();
authzException.put(OAuth2Exception.ERROR, "error");
authzException.put(OAuth2Exception.DESCRIPTION,"Couldn't find encrypter for client: " + clientDetails.getClientId());
return JsonUtils.gson2Json(authzException);
}
} else {
if (clientDetails.getUserInfoSigningAlgorithm()==null||clientDetails.getUserInfoSigningAlgorithm().equals("none")) {
// unsigned ID token
//userInfoJWT = new PlainJWT(userInfoJWTClaims);
userJson=JsonUtils.gson2Json(claimsFields);
} else {
// signed ID token
if (signingAlg.equals(JWSAlgorithm.HS256)
|| signingAlg.equals(JWSAlgorithm.HS384)
|| signingAlg.equals(JWSAlgorithm.HS512)) {
// sign it with the client's secret
String client_secret=ReciprocalUtils.decoder(clientDetails.getClientSecret());
JwtSigningAndValidationService symmetricJwtSignerService =symmetricJwtSignerServiceBuilder.serviceBuilder(client_secret);
if(symmetricJwtSignerService!=null){
userInfoJWTClaims = new JWTClaimsSet.Builder(userInfoJWTClaims).claim("kid", "SYMMETRIC-KEY").build();
userInfoJWT = new SignedJWT(new JWSHeader(signingAlg), userInfoJWTClaims);
symmetricJwtSignerService.signJwt((SignedJWT) userInfoJWT);
}else{
_logger.error("Couldn't create symmetric validator for client " + clientDetails.getClientId() + " without a client secret");
}
} else {
userInfoJWTClaims = new JWTClaimsSet.Builder(userInfoJWTClaims).claim("kid", jwtSignerValidationService.getDefaultSignerKeyId()).build();
userInfoJWT = new SignedJWT(new JWSHeader(signingAlg), userInfoJWTClaims);
// sign it with the server's key
jwtSignerValidationService.signJwt((SignedJWT) userInfoJWT);
}
userJson=userInfoJWT.serialize();
}
}
return userJson;
}catch(OAuth2Exception e){
HashMap<String,Object>authzException=new HashMap<String,Object>();
authzException.put(OAuth2Exception.ERROR, e.getOAuth2ErrorCode());
authzException.put(OAuth2Exception.DESCRIPTION,e.getMessage());
return JsonUtils.object2Json(authzException);
}
}
public String accessTokenFormatError(String access_token){
HashMap<String,Object>atfe=new HashMap<String,Object>();
atfe.put(OAuth2Exception.ERROR, "token Format Invalid");
atfe.put(OAuth2Exception.DESCRIPTION, "access Token Format Invalid , access_token : "+access_token);
return JsonUtils.object2Json(atfe);
}
public UserInfo queryUserInfo(String uid){
_logger.debug("uid : "+uid);
UserInfo queryUserInfo=new UserInfo();
queryUserInfo.setUsername(uid);
UserInfo userInfo = (UserInfo) userInfoService.load(queryUserInfo);
return userInfo;
}
public void setOauth20tokenServices(DefaultTokenServices oauth20tokenServices) {
this.oauth20tokenServices = oauth20tokenServices;
}
public void setUserInfoService(UserInfoService userInfoService) {
this.userInfoService = userInfoService;
}
//
//
// public void setJwtSignerValidationService(
// JwtSigningAndValidationService jwtSignerValidationService) {
// this.jwtSignerValidationService = jwtSignerValidationService;
// }
//
// public void setJwtEnDecryptionService(
// JwtEncryptionAndDecryptionService jwtEnDecryptionService) {
// this.jwtEnDecryptionService = jwtEnDecryptionService;
// }
}

View File

@@ -0,0 +1,8 @@
/**
*
*/
/**
* @author Crystal.Sea
*
*/
package org.maxkey.authz.oauth2.provider.userinfo.endpoint;

View File

@@ -1,7 +1,7 @@
/**
*
*/
package com.connsec.authz.oidc.idtoken;
package org.maxkey.authz.oidc.idtoken;
import java.util.Arrays;
import java.util.Date;
@@ -10,25 +10,25 @@ import java.util.UUID;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.maxkey.authz.oauth2.common.DefaultOAuth2AccessToken;
import org.maxkey.authz.oauth2.common.OAuth2AccessToken;
import org.maxkey.authz.oauth2.provider.ClientDetailsService;
import org.maxkey.authz.oauth2.provider.OAuth2Authentication;
import org.maxkey.authz.oauth2.provider.OAuth2Request;
import org.maxkey.authz.oauth2.provider.token.TokenEnhancer;
import org.maxkey.config.oidc.OIDCProviderMetadata;
import org.maxkey.crypto.ReciprocalUtils;
import org.maxkey.crypto.jwt.encryption.service.JwtEncryptionAndDecryptionService;
import org.maxkey.crypto.jwt.encryption.service.impl.RecipientJwtEncryptionAndDecryptionServiceBuilder;
import org.maxkey.crypto.jwt.signer.service.JwtSigningAndValidationService;
import org.maxkey.crypto.jwt.signer.service.impl.SymmetricSigningAndValidationServiceBuilder;
import org.maxkey.domain.apps.oauth2.provider.ClientDetails;
import org.maxkey.web.WebContext;
import com.nimbusds.jose.util.Base64URL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.connsec.authz.oauth2.common.DefaultOAuth2AccessToken;
import com.connsec.authz.oauth2.common.OAuth2AccessToken;
import com.connsec.authz.oauth2.provider.ClientDetailsService;
import com.connsec.authz.oauth2.provider.OAuth2Authentication;
import com.connsec.authz.oauth2.provider.OAuth2Request;
import com.connsec.authz.oauth2.provider.token.TokenEnhancer;
import com.connsec.config.oidc.OIDCProviderMetadata;
import com.connsec.crypto.ReciprocalUtils;
import com.connsec.crypto.jwt.encryption.service.JwtEncryptionAndDecryptionService;
import com.connsec.crypto.jwt.encryption.service.impl.RecipientJwtEncryptionAndDecryptionServiceBuilder;
import com.connsec.crypto.jwt.signer.service.JwtSigningAndValidationService;
import com.connsec.crypto.jwt.signer.service.impl.SymmetricSigningAndValidationServiceBuilder;
import com.connsec.domain.apps.oauth2.provider.ClientDetails;
import com.connsec.web.WebContext;
import com.google.common.base.Strings;
import com.nimbusds.jose.EncryptionMethod;
import com.nimbusds.jose.JWEAlgorithm;
@@ -87,22 +87,22 @@ public class OIDCIdTokenEnhancer implements TokenEnhancer {
if (request.getScope().contains(ID_TOKEN_SCOPE)) {//Enhance for OpenID Connect
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(authentication.getOAuth2Request().getClientId());
JWTClaimsSet.Builder builder=new JWTClaimsSet.Builder();
builder.subject(authentication.getName())
.expirationTime(accessToken.getExpiration())
.claim(providerMetadata.getIssuer(), true)
.issueTime(new Date())
.audience(Arrays.asList(authentication.getOAuth2Request().getClientId()))
.jwtID(UUID.randomUUID().toString());
JWTClaimsSet idClaims = new JWTClaimsSet.Builder()
.subject(authentication.getName())
.expirationTime(accessToken.getExpiration())
.claim(providerMetadata.getIssuer(), true)
.issueTime(new Date())
.audience(Arrays.asList(authentication.getOAuth2Request().getClientId()))
.jwtID(UUID.randomUUID().toString())
.build();
/**
* https://self-issued.me
* @see http://openid.net/specs/openid-connect-core-1_0.html#SelfIssuedDiscovery
* 7. Self-Issued OpenID Provider
*/
if(providerMetadata.getIssuer().equalsIgnoreCase("https://self-issued.me")){
idClaims.setCustomClaim("sub_jwk", jwtSignerService.getAllPublicKeys().get(jwtSignerService.getDefaultSignerKeyId()));
builder.claim("sub_jwk", jwtSignerService.getAllPublicKeys().get(jwtSignerService.getDefaultSignerKeyId()));
}
// if the auth time claim was explicitly requested OR if the client always wants the auth time, put it in
@@ -110,24 +110,24 @@ public class OIDCIdTokenEnhancer implements TokenEnhancer {
|| (request.getExtensions().containsKey("idtoken")) // TODO: parse the ID Token claims (#473) -- for now assume it could be in there
) {
DateTime loginDate=DateTime.parse(WebContext.getUserInfo().getLastLoginTime(), DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
idClaims.setClaim("auth_time", loginDate.getMillis()/ 1000);
builder.claim("auth_time", loginDate.getMillis()/ 1000);
}
String nonce = (String)request.getExtensions().get("nonce");
if (!Strings.isNullOrEmpty(nonce)) {
idClaims.setCustomClaim("nonce", nonce);
builder.claim("nonce", nonce);
}
JWSAlgorithm signingAlg = jwtSignerService.getDefaultSigningAlgorithm();
SignedJWT signed = new SignedJWT(new JWSHeader(signingAlg), idClaims);
SignedJWT signed = new SignedJWT(new JWSHeader(signingAlg), builder.build());
Set<String> responseTypes = request.getResponseTypes();
if (responseTypes.contains("token")) {
// calculate the token hash
Base64URL at_hash = IdTokenHashUtils.getAccessTokenHash(signingAlg, signed);
idClaims.setClaim("at_hash", at_hash);
builder.claim("at_hash", at_hash);
}
logger.debug("idClaims "+idClaims);
logger.debug("idClaims "+builder.build());
JWT idToken=null;
if (clientDetails.getIdTokenEncryptedAlgorithm() != null && !clientDetails.getIdTokenEncryptedAlgorithm().equals("none")
@@ -140,7 +140,7 @@ public class OIDCIdTokenEnhancer implements TokenEnhancer {
if (recipientJwtEnDecryptionService != null) {
JWEAlgorithm jweAlgorithm=new JWEAlgorithm(clientDetails.getIdTokenEncryptedAlgorithm());
EncryptionMethod encryptionMethod=new EncryptionMethod(clientDetails.getIdTokenEncryptionMethod());
EncryptedJWT encryptedJWT = new EncryptedJWT(new JWEHeader(jweAlgorithm, encryptionMethod), idClaims);
EncryptedJWT encryptedJWT = new EncryptedJWT(new JWEHeader(jweAlgorithm, encryptionMethod), builder.build());
recipientJwtEnDecryptionService.encryptJwt(encryptedJWT);
idToken=encryptedJWT;
}else{
@@ -149,7 +149,7 @@ public class OIDCIdTokenEnhancer implements TokenEnhancer {
} else {
if (signingAlg==null||signingAlg.equals("none")) {
// unsigned ID token
idToken = new PlainJWT(idClaims);
idToken = new PlainJWT(builder.build());
} else {
// signed ID token
if (signingAlg.equals(JWSAlgorithm.HS256)
@@ -160,15 +160,15 @@ public class OIDCIdTokenEnhancer implements TokenEnhancer {
JwtSigningAndValidationService symmetricJwtSignerService =symmetricJwtSignerServiceBuilder.serviceBuilder(client_secret);
if(symmetricJwtSignerService!=null){
idClaims.setCustomClaim("kid", "SYMMETRIC-KEY");
idToken = new SignedJWT(new JWSHeader(signingAlg), idClaims);
builder.claim("kid", "SYMMETRIC-KEY");
idToken = new SignedJWT(new JWSHeader(signingAlg), builder.build());
symmetricJwtSignerService.signJwt((SignedJWT) idToken);
}else {
logger.error("Couldn't create symmetric validator for client " + clientDetails.getClientId() + " without a client secret");
}
} else {
idClaims.setCustomClaim("kid", jwtSignerService.getDefaultSignerKeyId());
idToken = new SignedJWT(new JWSHeader(signingAlg), idClaims);
builder.claim("kid", jwtSignerService.getDefaultSignerKeyId());
idToken = new SignedJWT(new JWSHeader(signingAlg), builder.build());
// sign it with the server's key
jwtSignerService.signJwt((SignedJWT) idToken);
}