OAuth
https://playframework.com/documentation/2.6.x/JavaOAuth
OAuth是一个发布和交互受保护数据的简单方法。这是一种更加安全的访问方式。举个例子,你可以去获取用户在Twitter上的数据。
OAuth有两个版本,1.0和2.0。第二个版本在不使用类库的情况下也很容易实现。因此Play只对1.0版本进行了支持。
使用
添加依赖
libraryDependencies ++= Seq(
ws
)
需要的信息
OAuth需要你在服务提供商进行注册。确保检查你的回调URL,当不匹配是服务提供商有可能会拒绝你的调用。当在本地工作时,可以使用/etc/hosts/来伪造一个本地机器的域名
服务提供商会提供:
- Application ID
- Secret key
- Request Token URL
- Access Token URL
- Authorize URL
认证流程
大部分流程都会由Play类库来完成
1. 从服务端获取request token(在服务端到服务端的调用中)
2. 将用户重定向到服务提供商,这里用户会对你的应用进行授权
3. 服务提供商会将用户重定向返回,并提供一个verifier
4. 根据verifier,交换request token和access token
access token可以传递给任何需要获取保护数据的请求。
可以在The OAuth Bible获取更多细节。
Example
conf/routers
GET /twitter/homeTimeline controllers.Twitter.homeTimeline()
GET /twitter/auth controllers.Twitter.auth()
controller
import play.libs.oauth.OAuth;
import play.libs.oauth.OAuth.ConsumerKey;
import play.libs.oauth.OAuth.OAuthCalculator;
import play.libs.oauth.OAuth.RequestToken;
import play.libs.oauth.OAuth.ServiceInfo;
import play.libs.ws.WSClient;
import play.mvc.Controller;
import play.mvc.Result;
import com.google.common.base.Strings;
import javax.inject.Inject;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
public class Twitter extends Controller {
static final ConsumerKey KEY = new ConsumerKey("...", "...");
private static final ServiceInfo SERVICE_INFO =
new ServiceInfo("https://api.twitter.com/oauth/request_token",
"https://api.twitter.com/oauth/access_token",
"https://api.twitter.com/oauth/authorize",
KEY);
private static final OAuth TWITTER = new OAuth(SERVICE_INFO);
private final WSClient ws;
@Inject
public Twitter(WSClient ws) {
this.ws = ws;
}
public CompletionStage<Result> homeTimeline() {
Optional<RequestToken> sessionTokenPair = getSessionTokenPair();
if (sessionTokenPair.isPresent()) {
return ws.url("https://api.twitter.com/1.1/statuses/home_timeline.json")
.sign(new OAuthCalculator(Twitter.KEY, sessionTokenPair.get()))
.get()
.thenApply(result -> ok(result.asJson()));
}
return CompletableFuture.completedFuture(redirect(routes.Twitter.auth()));
}
public Result auth() {
String verifier = request().getQueryString("oauth_verifier");
if (Strings.isNullOrEmpty(verifier)) {
String url = routes.Twitter.auth().absoluteURL(request());
RequestToken requestToken = TWITTER.retrieveRequestToken(url);
saveSessionTokenPair(requestToken);
return redirect(TWITTER.redirectUrl(requestToken.token));
} else {
RequestToken requestToken = getSessionTokenPair().get();
RequestToken accessToken = TWITTER.retrieveAccessToken(requestToken, verifier);
saveSessionTokenPair(accessToken);
return redirect(routes.Twitter.homeTimeline());
}
}
private void saveSessionTokenPair(RequestToken requestToken) {
session("token", requestToken.token);
session("secret", requestToken.secret);
}
private Optional<RequestToken> getSessionTokenPair() {
if (session().containsKey("token")) {
return Optional.ofNullable(new RequestToken(session("token"), session("secret")));
}
return Optional.empty();
}
}
OAuth不能抵御任何MITM攻击(https://en.wikipedia.org/wiki/Man-in-the-middle_attack)。这个例子中将token和secret保存到会话cookie中,通过play.http.session.secure=true配置来启用https来获取最好的保护。 |