Skip to content

Instantly share code, notes, and snippets.

@yusuke2255
Created March 20, 2015 05:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yusuke2255/a277b49e1e0af2953848 to your computer and use it in GitHub Desktop.
Save yusuke2255/a277b49e1e0af2953848 to your computer and use it in GitHub Desktop.
【メモ】DropwizardでCookieのsession idで認証してみる ref: http://qiita.com/Kawata/items/2e8453ac149e365f762a
plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '1.2.0'
}
apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'application'
defaultTasks 'shadowJar'
project.ext {
authorName = 'Hogeo HOGETA'
dropwizardVersion = '0.8.0'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
group = 'jp.hoge.app'
version = '0.1'
applicationName = 'hoge-session-auth'
mainClassName = 'jp.hoge.app.HogeApplication'
repositories {
mavenLocal()
mavenCentral()
}
def defaultEncoding = 'UTF-8'
tasks.withType(AbstractCompile)*.options*.encoding = defaultEncoding
tasks.withType(GroovyCompile)*.groovyOptions*.encoding = defaultEncoding
dependencies {
// 全部は要らないけど消すのめんどいんで
compile "io.dropwizard:dropwizard-core:${dropwizardVersion}"
compile "io.dropwizard:dropwizard-assets:${dropwizardVersion}"
compile "io.dropwizard:dropwizard-views:${dropwizardVersion}"
compile "io.dropwizard:dropwizard-views-freemarker:${dropwizardVersion}"
compile "io.dropwizard:dropwizard-migrations:${dropwizardVersion}"
compile "io.dropwizard:dropwizard-hibernate:${dropwizardVersion}"
compile "io.dropwizard:dropwizard-jdbi:${dropwizardVersion}"
compile "io.dropwizard:dropwizard-auth:${dropwizardVersion}"
testCompile "junit:junit:4.11"
compile "io.dropwizard:dropwizard-testing:${dropwizardVersion}"
}
task wrapper(type: Wrapper) {
gradleVersion = '2.2'
}
shadowJar {
archiveName = String.format("%s-%s.jar", applicationName, version)
mergeServiceFiles()
exclude 'META-INF/*.SF'
exclude 'META-INF/*.DSA'
exclude 'META-INF/*.RSA'
}
jar {
manifest {
attributes(
'Implementation-Title': applicationName,
'Implementation-Version': version,
'Built-By': authorName,
'Built-Time': new Date(),
'Main-Class': mainClassName,
'Class-Path': configurations.compile.collect { it.getName() }.join(' ')
)
}
}
/* TODO コードとか作りとか色々汚いので使うときにはちゃんと修正する */
# the key needs to match the suffix of the renderer
viewRendererConfiguration:
.ftl:
strict_syntax: yes
whitespace_stripping: yes
server:
applicationConnectors:
- type: http
port: 8082
- type: https
port: 8443
validateCerts: false
adminConnectors:
- type: http
port: 8084
- type: https
port: 8444
validateCerts: false
package jp.hoge.app.auth;
import com.google.common.base.Optional;
import jp.hoge.app.core.HogeUser;
import io.dropwizard.auth.AuthenticationException;
import io.dropwizard.auth.Authenticator;
public class HogeSessionAuthenticator implements Authenticator<HogeTokenCredentials, HogeUser> {
@Override
public Optional<HogeUser> authenticate(HogeTokenCredentials credentials)
throws AuthenticationException {
// 本来はredisとかに問い合わせて有効か確認してユーザ情報を取得する
if ("abcdefg".equals(credentials.getToken())) {
return Optional.of(new HogeUser("myID"));
}
return Optional.absent();
}
}
package jp.hoge.app.auth;
public class HogeTokenCredentials {
private final String token;
public HogeTokenCredentials(String token) {
this.token = token;
}
public String getToken() {
return token;
}
}
package jp.hoge.app.auth;
import java.net.URI;
import java.net.URISyntaxException;
import javax.ws.rs.core.Response;
import io.dropwizard.auth.UnauthorizedHandler;
public class HogeUnauthorizedHandler implements UnauthorizedHandler {
@Override
public Response buildResponse(String prefix, String realm) {
try {
// ログイン画面にリダイレクト
return Response.seeOther(new URI("/login")).build();
} catch (URISyntaxException e) {
e.printStackTrace();
}
return Response.ok().build();
}
}
package jp.hoge.app.auth;
import java.util.Arrays;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import io.dropwizard.auth.AuthFactory;
import io.dropwizard.auth.AuthenticationException;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.UnauthorizedHandler;
/**
* ほぼほぼio.dropwizard.auth.basic.BasicAuthFactoryの写経でいけた
*/
public class SessionUserAuthFactory<T> extends
AuthFactory<HogeTokenCredentials, T> {
@Context
private HttpServletRequest request;
private final boolean required;
private UnauthorizedHandler unauthorizedHandler = new HogeUnauthorizedHandler();
private final Class<T> generatedClass;
private String prefix = "Sid"; // 使わないけど
private final String realm; // 使わないけど
public SessionUserAuthFactory(
Authenticator<HogeTokenCredentials, T> authenticator,
final String realm,
final Class<T> generatedClass) {
super(authenticator);
this.required = false;
this.realm = realm;
this.generatedClass = generatedClass;
}
private SessionUserAuthFactory(final boolean required,
final Authenticator<HogeTokenCredentials, T> authenticator,
final String realm,
final Class<T> generatedClass) {
super(authenticator);
this.required = required;
this.realm = realm;
this.generatedClass = generatedClass;
}
public SessionUserAuthFactory<T> responseBuilder(UnauthorizedHandler unauthorizedHandler) {
this.unauthorizedHandler = unauthorizedHandler;
return this;
}
@Override
public T provide() {
// ラムダ式で書いてみたかったの (正しいのか知らん)
Optional<HogeTokenCredentials> credentials = Optional.ofNullable(request)
.map(req -> req.getCookies())
.map(cookies -> Arrays.asList(cookies).stream()
.filter(cookie -> "sid".equals(cookie.getName()))
.findFirst().orElseGet(null))
.map(sidcookie -> sidcookie.getValue())
.map(sid -> new HogeTokenCredentials(sid));
if (credentials.isPresent()) {
try {
// guava出てきてもうた
final com.google.common.base.Optional<T> result = authenticator().authenticate(credentials.get());
if (result.isPresent()) return result.get();
} catch (AuthenticationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 認証必須のリソースの場合はUnauthorizedHandlerを利用してログイン画面に飛ばす
if (required) {
throw new WebApplicationException(unauthorizedHandler.buildResponse(prefix, realm));
}
return null;
}
@Override
public void setRequest(HttpServletRequest request) {
this.request = request;
}
@Override
public AuthFactory<HogeTokenCredentials, T> clone(boolean required) {
return new SessionUserAuthFactory<>(required, authenticator(),this.realm, this.generatedClass).responseBuilder(unauthorizedHandler);
}
@Override
public Class<T> getGeneratedClass() {
return generatedClass;
}
}
package jp.hoge.app.core;
public class HogeUser {
private final String userId;
public HogeUser(String userId) {
this.userId = userId;
}
public String getUserId() {
return userId;
}
}
package jp.hoge.app;
import jp.hoge.app.auth.HogeSessionAuthenticator;
import jp.hoge.app.auth.SessionUserAuthFactory;
import jp.hoge.app.core.HogeUser;
import jp.hoge.app.resources.LoginResource;
import jp.hoge.app.resources.HogeUserResource;
import com.google.common.collect.ImmutableMap;
import io.dropwizard.Application;
import io.dropwizard.auth.AuthFactory;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import io.dropwizard.views.ViewBundle;
public class HogeApplication extends Application<HogeConfiguration> {
public static void main(String[] args) throws Exception {
new HogeApplication().run(args);
}
@Override
public String getName() {
return "hoge-session-auth";
}
@Override
public void initialize(Bootstrap<HogeConfiguration> bootstrap) {
bootstrap.addBundle(new ViewBundle<HogeConfiguration>() {
@Override
public ImmutableMap<String, ImmutableMap<String, String>> getViewConfiguration(HogeConfiguration config) {
return config.getViewRendererConfiguration();
}
});
{
}
@Override
public void run(HogeConfiguration configuration,Environment environment) throws Exception {
environment.jersey().register(AuthFactory.binder(new SessionUserAuthFactory<HogeUser>(new HogeSessionAuthenticator(), "HOGE USER", HogeUser.class)));
final LoginResource loginResource = new LoginResource();
environment.jersey().register(loginResource);
final HogeUserResource hogeUserResource = new HogeUserResource();
environment.jersey().register(hogeUserResource);
}
}
package jp.hoge.app;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.ImmutableMap;
import io.dropwizard.Configuration;
public class HogeConfiguration extends Configuration {
@NotNull
private ImmutableMap<String, ImmutableMap<String, String>> viewRendererConfiguration = ImmutableMap.of();
@JsonProperty("viewRendererConfiguration")
public ImmutableMap<String, ImmutableMap<String, String>> getViewRendererConfiguration() {
return viewRendererConfiguration;
}
}
package jp.hoge.app.resources;
import io.dropwizard.auth.Auth;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import jp.hoge.app.core.HogeUser;
import jp.hoge.app.views.MypageTopView;
@Path("/mypage")
public class HogeUserResource {
@GET
@Path("/")
@Produces(MediaType.TEXT_HTML)
public MypageTopView getMypageView(@Auth HogeUser user) {
// @AuthでCookie認証後にUserが取得される
return new MypageTopView(MypageTopView.Template.FREEMARKER,user);
}
}
package jp.hoge.app.resources;
import java.net.URI;
import java.net.URISyntaxException;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response;
import jp.hoge.app.views.LoginView;
@Path("/login")
public class LoginResource {
@GET
@Path("/")
@Produces(MediaType.TEXT_HTML)
public LoginView getLoginView() {
return new LoginView(LoginView.Template.FREEMARKER);
}
@POST
@Path("/signin")
public Response signup(@FormParam("email") String email , @FormParam("password") String password) {
// アイパス認証
if ("test".equals(email) && "test".equals(password)) {
// Cookie生成 (これだとブラウザ閉じるまでの間だけ)
NewCookie cookies = new NewCookie("sid","abcdefg","/",null,"",NewCookie.DEFAULT_MAX_AGE,false);;
try {
// Cookieセットしてマイページ画面へリダイレクト
return Response.seeOther(new URI("/mypage")).cookie(cookies).build();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
try {
// 認証失敗したらログイン画面へ戻る
return Response.seeOther(new URI("/login")).build();
} catch (URISyntaxException e) {
e.printStackTrace();
}
return Response.serverError().build();
}
}
package jp.hoge.app.views;
import io.dropwizard.views.View;
public class LoginView extends View {
public enum Template {
FREEMARKER("login.ftl");
private String templateName;
private Template(String templateName){
this.templateName = templateName;
}
public String getTemplateName(){
return templateName;
}
}
public LoginView(LoginView.Template templateName) {
super(templateName.getTemplateName());
}
}
package jp.hoge.app.views;
import jp.hoge.app.core.HogeUser;
import io.dropwizard.views.View;
public class MypageTopView extends View {
private final HogeUser user;
public enum Template {
FREEMARKER("mypage_top.ftl");
private String templateName;
private Template(String templateName){
this.templateName = templateName;
}
public String getTemplateName(){
return templateName;
}
}
public MypageTopView(MypageTopView.Template templateName,HogeUser user) {
super(templateName.getTemplateName());
this.user = user;
}
public HogeUser getUser() {
return user;
}
}
<#-- @ftlvariable name="" type="jp.hoge.app.views.LoginView" -->
<html>
<head><meta charset="UTF-8"></head>
<body>
<h1>ログイン</h1>
<form action="login/signup" method="post">
<p>Eメール : <input type="text" name="email" size="40"></p>
<p>パスワード : <input type="password" name="password" size="40"></p>
<input type="submit" value="ログイン">
</form>
</body>
</html>
<#-- @ftlvariable name="" type="jp.hoge.app.views.LoginView" -->
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<!-- calls getUser().getUserId() and sanitizes it -->
<h1>Hello, ${user.userId?html}!</h1>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment