Spring : Google 身份验证 redirect_uri_mismatch 和 URL 无法在浏览器中打开
Spring : Google authentication redirect_uri_mismatch and URL wont open on browser
我正在 tomcat 上开发 Spring-MVC 应用程序 运行,我想在其中使用 Google 驱动功能。我尝试在本地机器上使用服务帐户,没有遇到任何问题。但是当我在服务器上上传代码时,浏览器 URL 不会打开。然后我想,我不应该使用服务帐户,我应该使用普通的网络应用程序帐户。现在当我这样做时,我得到一个 redirect_uri_mismatch.
我不明白一件事,我在流程中设置了重定向 URL,在 JSON 中,为什么它会得到带有随机端口号的 redirect_url .如果我在浏览器中更改端口号 URL,它工作正常。但它仍然在服务器上无法打开浏览器 url,我可以在 tomcat 日志中看到它,但该死的东西不会打开 URL。
这是我从 Google 应用重定向 URL :
http://localhost/authorizeuser
http://localhost:8080/
http://localhost:8080
http://localhost
http://localhost:8080/Callback
https://testserver.net/Callback
http://testserver.net/Callback
http://127.0.0.1
这是我的 client_secret.json :
{"web": {
"client_id": "clientid",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_email": "clientemailstuff",
"client_x509_cert_url": "certurlstuff",
"client_secret": "itsasecret",
"redirect_uris": ["http://localhost:8080/","http://localhost:8080"],
"javascript_origins": ["https://testserver.net", "http://testserver.net","http://localhost:8080"]
}}
这是我尝试进行身份验证的代码:
@Override
public Credential authorize() throws IOException {
InputStream in =
DriveQuickstartImpl.class.getResourceAsStream("/client_secret.json");
GoogleClientSecrets clientSecrets =
GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
GoogleAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(DATA_STORE_FACTORY)
.setAccessType("offline")
.build();
flow.newAuthorizationUrl().setState("xyz").setRedirectUri("http://localhost:8080/Callback");
Credential credential = new AuthorizationCodeInstalledApp(
flow, new LocalServerReceiver()).authorize("user");
if(credential!=null && credential.getRefreshToken() != null){
storeCredentials(credential);
}
return credential;
}
这让我很生气,因为我正在设置重定向 url,它只是被忽略了,为什么在服务器上部署应用程序时浏览器选项卡不会打开。
更新
Spring 问题也已修复,下面的代码可用于 Google 在具有 tomcat 或其他服务器上的驱动器授权。
@Service
@Transactional
public class GoogleAuthorization{
@Autowired
private DriveQuickstart driveQuickstart;
private static final String APPLICATION_NAME ="APPNAME";
private static final java.io.File DATA_STORE_DIR = new java.io.File(
"/home/deploy/store");
private static FileDataStoreFactory DATA_STORE_FACTORY;
private static final JsonFactory JSON_FACTORY =
JacksonFactory.getDefaultInstance();
private static HttpTransport HTTP_TRANSPORT;
private static final List<String> SCOPES =
Arrays.asList(DriveScopes.DRIVE);
private static final String clientid = "clientid";
private static final String clientsecret = "clientsecret";
private static final String CALLBACK_URI = "http://localhost:8080/getgooglelogin";
private String stateToken;
private final GoogleAuthorizationCodeFlow flow;
public GoogleAuthorization(){
try {
HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR);
} catch (GeneralSecurityException | IOException e) {
e.printStackTrace();
}
flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT,
JSON_FACTORY, clientid, clientsecret, SCOPES).setAccessType("offline").setApprovalPrompt("force").build();
generateStateToken();
}
/**
* Builds a login URL based on client ID, secret, callback URI, and scope
*/
public String buildLoginUrl() {
final GoogleAuthorizationCodeRequestUrl url = flow.newAuthorizationUrl();
return url.setRedirectUri(CALLBACK_URI).setState(stateToken).build();
}
/**
* Generates a secure state token
*/
private void generateStateToken(){
SecureRandom sr1 = new SecureRandom();
stateToken = "google;"+sr1.nextInt();
}
/**s
* Accessor for state token
*/
public String getStateToken(){
return stateToken;
}
/**
* Expects an Authentication Code, and makes an authenticated request for the user's profile information
* * @param authCode authentication code provided by google
*/
public void saveCredentials(final String authCode) throws IOException {
GoogleTokenResponse response = flow.newTokenRequest(authCode).setRedirectUri(CALLBACK_URI).execute();
Credential credential = flow.createAndStoreCredential(response, null);
System.out.println(" Credential access token is "+credential.getAccessToken());
System.out.println("Credential refresh token is "+credential.getRefreshToken());
// The line below gives me a NPE.
this.driveQuickstart.storeCredentials(credential);
}
}
控制器方法:
@RequestMapping(value = "/getgooglelogin")
public String getGoogleLogin(HttpServletRequest request, HttpServletResponse response, HttpSession session,Model model) {
// Below guy should be autowired if you want to use Spring.
GoogleAuthorization helper = new GoogleAuthorization();
if (request.getParameter("code") == null
|| request.getParameter("state") == null) {
model.addAttribute("URL", helper.buildLoginUrl());
session.setAttribute("state", helper.getStateToken());
} else if (request.getParameter("code") != null && request.getParameter("state") != null && request.getParameter("state").equals(session.getAttribute("state"))) {
session.removeAttribute("state");
try {
helper.saveCredentials(request.getParameter("code"));
return "redirect:/dashboard";
} catch (IOException e) {
e.printStackTrace();
}
}
return "newjsp";
}
newjsp只是有一个按钮可以点击URL。
具体来说,您获得随机端口是因为您使用 LocalServerReceiver, which starts up a jetty instance on a free port 来接收授权码。
在更高层次上,您似乎正在开发 web server application, but you are trying to use Google OAuth as if it were an installed application. If you are indeed making a web server application, you should be using your server's host name instead of localhost in your callback URL, providing a link for the end user to authenticate using flow.newAuthorizationUrl(), and have your callback fetch the token using flow.newTokenRequest(String). Also make sure that the Client ID you created in your console is of type Web application, or you'll get redirect_uri_mismatch errors. A full working example of how to do this can be found here。
而不是使用:
Credential credential = new AuthorizationCodeInstalledApp( flow,
new LocalServerReceiver).authorize("user");
使用
LocalServerReceiver localReceiver = new LocalServerReceiver.
Builder().setPort(XXXX).build();
用于设置一个静态端口号
Credential credential = new AuthorizationCodeInstalledApp( flow,
localReceiver).authorize("user");
虽然您无法更改重定向 url,但是您可以设置主机和端口。
要更改主机,请使用 .setHost()
方法
您还可以使用默认构造函数作为:
Credential credential = new AuthorizationCodeInstalledApp( flow,
new LocalServerReceiver("Host", XXXX).authorize("user");
我正在 tomcat 上开发 Spring-MVC 应用程序 运行,我想在其中使用 Google 驱动功能。我尝试在本地机器上使用服务帐户,没有遇到任何问题。但是当我在服务器上上传代码时,浏览器 URL 不会打开。然后我想,我不应该使用服务帐户,我应该使用普通的网络应用程序帐户。现在当我这样做时,我得到一个 redirect_uri_mismatch.
我不明白一件事,我在流程中设置了重定向 URL,在 JSON 中,为什么它会得到带有随机端口号的 redirect_url .如果我在浏览器中更改端口号 URL,它工作正常。但它仍然在服务器上无法打开浏览器 url,我可以在 tomcat 日志中看到它,但该死的东西不会打开 URL。
这是我从 Google 应用重定向 URL :
http://localhost/authorizeuser
http://localhost:8080/
http://localhost:8080
http://localhost
http://localhost:8080/Callback
https://testserver.net/Callback
http://testserver.net/Callback
http://127.0.0.1
这是我的 client_secret.json :
{"web": {
"client_id": "clientid",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_email": "clientemailstuff",
"client_x509_cert_url": "certurlstuff",
"client_secret": "itsasecret",
"redirect_uris": ["http://localhost:8080/","http://localhost:8080"],
"javascript_origins": ["https://testserver.net", "http://testserver.net","http://localhost:8080"]
}}
这是我尝试进行身份验证的代码:
@Override
public Credential authorize() throws IOException {
InputStream in =
DriveQuickstartImpl.class.getResourceAsStream("/client_secret.json");
GoogleClientSecrets clientSecrets =
GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
GoogleAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(DATA_STORE_FACTORY)
.setAccessType("offline")
.build();
flow.newAuthorizationUrl().setState("xyz").setRedirectUri("http://localhost:8080/Callback");
Credential credential = new AuthorizationCodeInstalledApp(
flow, new LocalServerReceiver()).authorize("user");
if(credential!=null && credential.getRefreshToken() != null){
storeCredentials(credential);
}
return credential;
}
这让我很生气,因为我正在设置重定向 url,它只是被忽略了,为什么在服务器上部署应用程序时浏览器选项卡不会打开。
更新 Spring 问题也已修复,下面的代码可用于 Google 在具有 tomcat 或其他服务器上的驱动器授权。
@Service
@Transactional
public class GoogleAuthorization{
@Autowired
private DriveQuickstart driveQuickstart;
private static final String APPLICATION_NAME ="APPNAME";
private static final java.io.File DATA_STORE_DIR = new java.io.File(
"/home/deploy/store");
private static FileDataStoreFactory DATA_STORE_FACTORY;
private static final JsonFactory JSON_FACTORY =
JacksonFactory.getDefaultInstance();
private static HttpTransport HTTP_TRANSPORT;
private static final List<String> SCOPES =
Arrays.asList(DriveScopes.DRIVE);
private static final String clientid = "clientid";
private static final String clientsecret = "clientsecret";
private static final String CALLBACK_URI = "http://localhost:8080/getgooglelogin";
private String stateToken;
private final GoogleAuthorizationCodeFlow flow;
public GoogleAuthorization(){
try {
HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR);
} catch (GeneralSecurityException | IOException e) {
e.printStackTrace();
}
flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT,
JSON_FACTORY, clientid, clientsecret, SCOPES).setAccessType("offline").setApprovalPrompt("force").build();
generateStateToken();
}
/**
* Builds a login URL based on client ID, secret, callback URI, and scope
*/
public String buildLoginUrl() {
final GoogleAuthorizationCodeRequestUrl url = flow.newAuthorizationUrl();
return url.setRedirectUri(CALLBACK_URI).setState(stateToken).build();
}
/**
* Generates a secure state token
*/
private void generateStateToken(){
SecureRandom sr1 = new SecureRandom();
stateToken = "google;"+sr1.nextInt();
}
/**s
* Accessor for state token
*/
public String getStateToken(){
return stateToken;
}
/**
* Expects an Authentication Code, and makes an authenticated request for the user's profile information
* * @param authCode authentication code provided by google
*/
public void saveCredentials(final String authCode) throws IOException {
GoogleTokenResponse response = flow.newTokenRequest(authCode).setRedirectUri(CALLBACK_URI).execute();
Credential credential = flow.createAndStoreCredential(response, null);
System.out.println(" Credential access token is "+credential.getAccessToken());
System.out.println("Credential refresh token is "+credential.getRefreshToken());
// The line below gives me a NPE.
this.driveQuickstart.storeCredentials(credential);
}
}
控制器方法:
@RequestMapping(value = "/getgooglelogin")
public String getGoogleLogin(HttpServletRequest request, HttpServletResponse response, HttpSession session,Model model) {
// Below guy should be autowired if you want to use Spring.
GoogleAuthorization helper = new GoogleAuthorization();
if (request.getParameter("code") == null
|| request.getParameter("state") == null) {
model.addAttribute("URL", helper.buildLoginUrl());
session.setAttribute("state", helper.getStateToken());
} else if (request.getParameter("code") != null && request.getParameter("state") != null && request.getParameter("state").equals(session.getAttribute("state"))) {
session.removeAttribute("state");
try {
helper.saveCredentials(request.getParameter("code"));
return "redirect:/dashboard";
} catch (IOException e) {
e.printStackTrace();
}
}
return "newjsp";
}
newjsp只是有一个按钮可以点击URL。
具体来说,您获得随机端口是因为您使用 LocalServerReceiver, which starts up a jetty instance on a free port 来接收授权码。
在更高层次上,您似乎正在开发 web server application, but you are trying to use Google OAuth as if it were an installed application. If you are indeed making a web server application, you should be using your server's host name instead of localhost in your callback URL, providing a link for the end user to authenticate using flow.newAuthorizationUrl(), and have your callback fetch the token using flow.newTokenRequest(String). Also make sure that the Client ID you created in your console is of type Web application, or you'll get redirect_uri_mismatch errors. A full working example of how to do this can be found here。
而不是使用:
Credential credential = new AuthorizationCodeInstalledApp( flow,
new LocalServerReceiver).authorize("user");
使用
LocalServerReceiver localReceiver = new LocalServerReceiver.
Builder().setPort(XXXX).build();
用于设置一个静态端口号
Credential credential = new AuthorizationCodeInstalledApp( flow,
localReceiver).authorize("user");
虽然您无法更改重定向 url,但是您可以设置主机和端口。
要更改主机,请使用 .setHost()
方法
您还可以使用默认构造函数作为:
Credential credential = new AuthorizationCodeInstalledApp( flow,
new LocalServerReceiver("Host", XXXX).authorize("user");