首先使用NetBeans建立一個新的Apache Wicket專案,並命名為myrp。依據原文所寫,作者將Web Application中負責和Open ID OP進行通訊的相關功能,放在model的套件中,其中分為兩個項目:
- model#RegistrationModel:實作各項需要Open ID OP所提供資料的bean類別。
//model#RegistrationModel.java package model; import java.io.Serializable; import java.util.Date; public class RegistrationModel implements Serializable { //這是自訂的使用者資訊類別 //未來可用在寫入資料庫的部份 private String openId; private String fullName; private String emailAddress; private String zipCode; private Date dateOfBirth; private String favoriteColor; public String getOpenId() { return openId; } public void setOpenId(String openId) { this.openId = openId; } public String getFullName() { return fullName; } public void setFullName(String fullName) { this.fullName = fullName; } public String getEmailAddress() { return emailAddress; } public void setEmailAddress(String emailAddress) { this.emailAddress = emailAddress; } public String getZipCode() { return zipCode; } public void setZipCode(String zipCode) { this.zipCode = zipCode; } public Date getDateOfBirth() { return dateOfBirth; } public void setDateOfBirth(Date dateOfBirth) { this.dateOfBirth = dateOfBirth; } public String getFavoriteColor() { return favoriteColor; } public void setFavoriteColor(String favoriteColor) { this.favoriteColor = favoriteColor; } }
- model#RegistrationService:將會使用到的openid4java函式庫中的各項方法以一類別來實作,並且提供static的呼叫方式。
//model#RegistrationService.java package model; import java.util.HashMap; import java.util.List; import org.apache.wicket.request.IRequestParameters; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.request.mapper.parameter.PageParameters.NamedPair; import org.joda.time.YearMonthDay; import org.openid4java.consumer.ConsumerManager; import org.openid4java.consumer.InMemoryConsumerAssociationStore; import org.openid4java.consumer.InMemoryNonceVerifier; import org.openid4java.consumer.VerificationResult; import org.openid4java.discovery.DiscoveryException; import org.openid4java.discovery.DiscoveryInformation; import org.openid4java.discovery.Identifier; import org.openid4java.message.AuthRequest; import org.openid4java.message.AuthSuccess; import org.openid4java.message.MessageExtension; import org.openid4java.message.ParameterList; import org.openid4java.message.sreg.SRegMessage; import org.openid4java.message.sreg.SRegRequest; import org.openid4java.message.sreg.SRegResponse; public class RegistrationService { private static ConsumerManager consumerManager; //進行OpenID的相關探索動作 @SuppressWarnings("unchecked") public static DiscoveryInformation performDiscoveryOnUserSuppliedIdentifier(String userSuppliedIdentifier) { DiscoveryInformation ret = null; ConsumerManager consumerManager = getConsumerManager(); try { // Perform discover on the User-Supplied Identifier List<DiscoveryInformation> discoveries = consumerManager.discover(userSuppliedIdentifier); // Pass the discoveries to the associate() method... ret = consumerManager.associate(discoveries); } catch (DiscoveryException e) { String message = "Error occurred during discovery!"; System.out.println(message); throw new RuntimeException(message, e); } return ret; } //建立OP進行驗證所需的AuthRequest物件 public static AuthRequest createOpenIdAuthRequest(DiscoveryInformation discoveryInformation, String returnToUrl) { AuthRequest ret = null; try { // Create the AuthRequest object ret = getConsumerManager().authenticate(discoveryInformation, returnToUrl); // Create the Simple Registration Request SRegRequest sRegRequest = SRegRequest.createFetchRequest(); sRegRequest.addAttribute("email", false); sRegRequest.addAttribute("fullname", false); sRegRequest.addAttribute("dob", false); sRegRequest.addAttribute("postcode", false); ret.addExtension(sRegRequest); } catch (Exception e) { String message = "Exception occurred while building AuthRequest object!"; System.out.println(message); throw new RuntimeException(message, e); } return ret; } //1.3.3的PageParameters具有Map功能 //1.5.3並沒有,所以要自己撰寫轉換程式 public static ParameterList toParameterList(PageParameters p) { HashMap<String, String> h = new HashMap<String, String>(); for (NamedPair pair : p.getAllNamed()) { h.put(pair.getKey(), pair.getValue()); } return new ParameterList(h); } public static ParameterList toParameterList(IRequestParameters rP) { HashMap<String, String> h = new HashMap<String, String>(); for (String name : rP.getParameterNames()) { h.put(name, rP.getParameterValue(name).toString()); } return new ParameterList(h); } //處理OP回傳的資訊 public static RegistrationModel processReturn(DiscoveryInformation discoveryInformation, PageParameters pageParameters, String returnToUrl) { RegistrationModel ret = null; // Verify the Information returned from the OP /// This is required according to the spec ParameterList response = new ParameterList(toParameterList(pageParameters)); try { VerificationResult verificationResult = getConsumerManager().verify(returnToUrl, response, discoveryInformation); Identifier verifiedIdentifier = verificationResult.getVerifiedId(); if (verifiedIdentifier != null) { AuthSuccess authSuccess = (AuthSuccess) verificationResult.getAuthResponse(); if (authSuccess.hasExtension(SRegMessage.OPENID_NS_SREG)) { MessageExtension extension = authSuccess.getExtension(SRegMessage.OPENID_NS_SREG); if (extension instanceof SRegResponse) { ret = new RegistrationModel(); ret.setOpenId(verifiedIdentifier.getIdentifier()); SRegResponse sRegResponse = (SRegResponse) extension; String value = sRegResponse.getAttributeValue("dob"); if (value != null) { ret.setDateOfBirth(new YearMonthDay(value).toDateMidnight().toDate()); } value = sRegResponse.getAttributeValue("email"); if (value != null) { ret.setEmailAddress(value); } value = sRegResponse.getAttributeValue("fullname"); if (value != null) { ret.setFullName(value); } value = sRegResponse.getAttributeValue("postcode"); if (value != null) { ret.setZipCode(value); } } } } } catch (Exception e) { String message = "Exception occurred while verifying response!"; System.out.println(message); throw new RuntimeException(message, e); } return ret; } //取得openid4java API中進行一連串驗證動作的物件 private static ConsumerManager getConsumerManager() { if (consumerManager == null) { consumerManager = new ConsumerManager(); consumerManager.setAssociations(new InMemoryConsumerAssociationStore()); consumerManager.setNonceVerifier(new InMemoryNonceVerifier(10000)); } return consumerManager; } //如果OP驗證使者成功,要導向的頁面(導向bookmarkable頁面)。 public static String getReturnToUrl() { return "http://localhost:8080/myrp/wicket/bookmarkable/myopenid.InformationReview?is_return=true"; } }
接下來就是網頁的部份,由Netbeans的Apache Wicket plugin所建立的wicket專案會自動將頁面進行一致化的編排,也就是會有共同的header及footer,因此所有的wicket page均要繼承BasePage此類別。
首先是本專案的Appilcation類別及MyRPSession類別,主要用來定義專案的相關屬性以及相關的資訊如何儲存在Session中。
//Application.java package myopenid; import org.apache.wicket.Session; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.request.Request; import org.apache.wicket.request.Response; public class Application extends WebApplication { public Application() { } public Class getHomePage() { return HomePage.class; } @Override public void init() { super.init(); this.getMarkupSettings().setDefaultMarkupEncoding("UTF-8"); } @Override public Session newSession(Request request, Response response) { //傳回自訂的Session物件,才能進行正確轉型的動作 return new MyRPSession(request); } }
//MyRPSession.java package myopenid; import org.apache.wicket.protocol.http.WebSession; import org.apache.wicket.request.Request; import org.openid4java.discovery.DiscoveryInformation; public class MyRPSession extends WebSession { //open-id DiscoveryInformation private DiscoveryInformation discoveryInformation; //是否已將DiscoveryInformation資訊寫入Session中 private boolean discoveryInformationStoredInSession; //指定存在Session中的key值 public static final String DISCOVERY_INFORMATION = "openid-disc"; public MyRPSession(Request request) { super(request); } //此方法指定DiscoveryInformation不存在Session中 public void setDiscoveryInformation(DiscoveryInformation discoveryInformation) { setDiscoveryInformation(discoveryInformation, false); } //可指定DiscoveryInformation是否儲存在Session中 public void setDiscoveryInformation(DiscoveryInformation discoveryInformation, boolean storeInSession) { this.discoveryInformation = discoveryInformation; if (storeInSession) { //將DiscoveryInformtion存在Session中 setAttribute(DISCOVERY_INFORMATION, discoveryInformation); discoveryInformationStoredInSession = true; } } public DiscoveryInformation getDiscoveryInformation() { DiscoveryInformation ret = discoveryInformation; //如果有設定將DiscoveryInformation存在Session中 //就回傳Session中的DiscoveryInfomation if (discoveryInformationStoredInSession) { ret = (DiscoveryInformation) getAttribute(DISCOVERY_INFORMATION); } return ret; } }
再來則是HomePage.html,首頁的html碼部份,它包含了輸入openid的表單(form)。
<!--HomePage.html--> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://git-wip-us.apache.org/repos/asf/wicket/repo?p=wicket.git;a=blob_plain;f=wicket-core/src/main/resources/META-INF/wicket-1.5.xsd;hb=master" xml:lang="en" lang="en"> <head> <!-- Apache Wicket 1.5x以上的xml schema xmlns:wicket="http://git-wip-us.apache.org/repos/asf/wicket/repo?p=wicket.git;a=blob_plain;f=wicket-core/src/main/resources/META-INF/wicket-1.5.xsd;hb=master" --> <wicket:head> <title>Open ID Relying Party</title> </wicket:head> </head> <body> <wicket:extend> <form id="OpenIdRegistrationForm" wicket:id="form"> <h1>請輸入你的OpenID以進入本站台!</h1> <br /> <table border="0"> <tr> <td><label>你的Open ID:</label></td> <td> <input class="biginput" type="text" wicket:id="openId"/> <input type="submit" wicket:id="confirmOpenIdButton" value="確認OpenID"/> </td> </tr> </table> </form> <br/><br/> </wicket:extend> </body> </html>
接下來則是其相對應的java原始碼,HomePage.java。
//HomePage.java package myopenid; import model.RegistrationModel; import model.RegistrationService; import org.apache.wicket.markup.html.form.Button; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.RequiredTextField; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.pages.RedirectPage; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.model.Model; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.openid4java.discovery.DiscoveryInformation; import org.openid4java.message.AuthRequest; public class HomePage extends BasePage { private String returnToUrl; public HomePage() { this(new PageParameters()); } public HomePage(PageParameters parameters) { returnToUrl = RegistrationService.getReturnToUrl(); //returnToUrl = urlFor(InformationReview.class, new PageParameters("is_return=true")).toString(); add(new OpenIdRegistrationForm("form", this, returnToUrl)); //add(new BookmarkablePageLink("mylink",InformationReview.class)); } public static class OpenIdRegistrationForm extends Form { public OpenIdRegistrationForm(String id, final HomePage owningPage, String returnToUrl) { this(id, owningPage, returnToUrl, new RegistrationModel()); } @SuppressWarnings("serial") public OpenIdRegistrationForm(String id, final HomePage owningPage, final String returnToUrl, final RegistrationModel formModel) { super(id); setModel(new CompoundPropertyModel(formModel)); TextField openId = new RequiredTextField("openId"); openId.setLabel(new Model("你的Open ID")); add(openId); Button confirmOpenIdButton = new Button("confirmOpenIdButton") { @Override public void onSubmit() { String userSuppliedIdentifier = formModel.getOpenId(); DiscoveryInformation discoveryInformation = RegistrationService.performDiscoveryOnUserSuppliedIdentifier(userSuppliedIdentifier); MyRPSession session = (MyRPSession) owningPage.getSession(); session.setDiscoveryInformation(discoveryInformation, true); AuthRequest authRequest = RegistrationService.createOpenIdAuthRequest(discoveryInformation, returnToUrl); //在Wicket 1..5版本以上只要呼叫setResponsePage即可進行redirect的動作 setResponsePage(new RedirectPage(authRequest.getDestinationUrl(true)).getPage()); } }; add(confirmOpenIdButton); } } }
最後則是OpenID OP驗證過後,會將資料傳回returnToUrl中所指定的網頁InformationReview.html,它包含了一個表單,可以讓使用者檢視資料是否正確。
<!--InformationReview.html--> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns:wicket="http://git-wip-us.apache.org/repos/asf/wicket/repo?p=wicket.git;a=blob_plain;f=wicket-core/src/main/resources/META-INF/wicket-1.5.xsd;hb=master"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>InformationReview</title> <link rel="stylesheet" type="text/css" href="style.css"/> </head> <body> <wicket:extend> <form id="OpenIdRegistrationSuccessForm" wicket:id="form"> <h1>Please review your registration info below.</h1> <br /> <table border="0"> <tr> <td><label>Your Open ID:</label></td> <td><input class="biginput" type="text" wicket:id="openId"/></td> </tr> <tr> <td><label>Full Name:</label></td> <td><input class="biginput" type="text" wicket:id="fullName"/></td> </tr> <tr> <td><label>Email Address:</label></td> <td><input class="biginput" type="text" wicket:id="emailAddress"/></td> </tr> <tr> <td><label>Zip Code:</label></td> <td><input class="biginput" type="text" wicket:id="zipCode"/></td> </tr> <tr> <td><label>Date of Birth:</label></td> <td><input class="biginput" type="text" wicket:id="dateOfBirth"/></td> </tr> <tr> <td> </td> <td><input class="bigbutton" type="submit" wicket:id="saveButton" value="Save"/></td> </tr> </table> </form> </wicket:extend> </body> </html>
相對應的InformationReview.java,裡面預留了將資料儲存在資料庫中的method。
//InformationReview.java package myopenid; import java.util.Date; import model.RegistrationModel; import model.RegistrationService; import org.apache.wicket.markup.html.form.Button; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.RequiredTextField; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.openid4java.discovery.DiscoveryInformation; public final class InformationReview extends BasePage { public InformationReview() { this(new PageParameters()); } public InformationReview(PageParameters pageParameters) { RegistrationModel registrationModel = new RegistrationModel(); if (!pageParameters.isEmpty()) { String isReturn = pageParameters.get("is_return").toString(); if (isReturn.equals("true")) { MyRPSession session = (MyRPSession) getSession(); DiscoveryInformation discoveryInformation = session.getDiscoveryInformation(); registrationModel = RegistrationService.processReturn(discoveryInformation, pageParameters, RegistrationService.getReturnToUrl()); if (registrationModel == null) { error("Open ID Confirmation Failed. No information was retrieved from the OpenID Provider. You will have to enter all information by hand into the text fields provided."); } } } add(new OpenIdRegistrationInformationDisplayForm("form", registrationModel)); } public static class OpenIdRegistrationInformationDisplayForm extends Form { @SuppressWarnings("serial") public OpenIdRegistrationInformationDisplayForm(String id, RegistrationModel registrationModel) { super(id, new CompoundPropertyModel(registrationModel)); TextField openId = new TextField("openId"); openId.setEnabled(false); add(openId); TextField fullName = new RequiredTextField("fullName"); add(fullName); TextField emailAddress = new RequiredTextField("emailAddress"); add(emailAddress); TextField zipCode = new TextField("zipCode"); add(zipCode); TextField dateOfBirth = new RequiredTextField("dateOfBirth", Date.class); add(dateOfBirth); Button saveButton = new Button("saveButton") { public void onSubmit() { if (saveRegistrationInfo()) { info("Registration Info saved."); } else { error("Registration Info could not be saved!"); } } }; add(saveButton); } private boolean saveRegistrationInfo() { return true; } } }
No comments:
Post a Comment