oauth的实现
单点登录技术方案
单点登录技术方案介绍单点登录(SSO)是一种身份验证和授权技术,允许用户只需一次登录即可访问多个相关系统和应用程序。
这减少了用户需要记住多个用户名和密码的负担,提高了系统的易用性和安全性。
本文将介绍单点登录技术的工作原理和常见的实现方案,以及其在企业应用中的优势和挑战。
工作原理单点登录的工作原理基于令牌(Token)的概念。
当用户成功登录主认证系统后,主认证系统会生成一个令牌并将其发送给用户的浏览器。
该令牌包含了用户的身份信息和有效期限等相关信息。
当用户尝试访问其他关联系统时,这些系统会将用户重定向到主认证系统,并携带令牌作为参数。
主认证系统收到请求后,验证令牌的合法性和有效期限。
如果令牌有效,主认证系统会向关联系统发送一个授权凭证,允许用户访问特定资源。
用户的浏览器将授权凭证传递给关联系统,以完成身份验证和授权过程。
实现方案基于Cookie的实现方案基于Cookie的单点登录是最常见的实现方式之一。
主认证系统在用户成功登录后,生成一个包含用户身份信息的加密Cookie,并将其发送给浏览器。
当用户尝试访问其他关联系统时,这些系统会在用户的浏览器中查找该Cookie,并验证其合法性和有效期。
如果Cookie有效,关联系统会完成身份验证,并允许用户访问特定资源。
由于Cookie存储在用户的浏览器中,因此存在一定的安全风险。
为了增强安全性,可以使用加密和签名技术对Cookie进行保护,防止被篡改或伪造。
基于Token的实现方案基于Token的单点登录是一种无状态的实现方式。
主认证系统在用户成功登录后,生成一个包含用户身份信息的令牌,并将其发送给浏览器。
令牌通常使用JSON Web令牌(JWT)的格式进行编码,包含了用户身份信息、有效期限等相关信息。
用户在访问其他关联系统时,将令牌作为参数携带在请求中。
关联系统在接收到请求后,通过验证令牌的合法性和有效期,完成身份验证和授权过程。
基于Token的单点登录相对于基于Cookie的实现更加安全,因为令牌不存储在用户的浏览器中,且可以通过加密和签名技术进行保护。
oauth2.0单点登录原理
oauth2.0单点登录原理
OAuth 2.0 是一种授权框架,用于授权第三方应用访问用户资源,而不需要用户提供其凭据。
单点登录(Single Sign-On,SSO)是一种身份验证和授权机制,允许用户使用单组凭据登录多个应用。
实现OAuth 2.0单点登录的原理如下:
1. 用户通过浏览器访问应用A,并尝试进行身份验证。
应用A检查用户是否已经进行了认证,并未认证的情况下将其重定向到认证服务器。
2. 认证服务器验证用户的身份,并要求用户提供其凭据(如用户名和密码)。
3. 认证服务器验证用户提供的凭据,并生成一个访问令牌(Access Token)。
4. 认证服务器将访问令牌发送回应用A。
此时,用户已经完成了对应用A的身份验证,并且应用A具有了访问令牌,可以访问用户的受保护资源。
5. 用户通过浏览器访问应用B,并尝试进行身份验证。
应用B检查用户是否已经进行了认证,并未认证的情况下将其重定向到认证服务器。
6. 认证服务器检测到用户已经具有一个有效的访问令牌,并生成一个新的访问令牌(或使用已有的访问令牌)。
7. 认证服务器将新的访问令牌发送回应用B。
8. 应用B使用新的访问令牌来访问用户的受保护资源。
通过这种方式,用户只需一次登录认证,就可以在多个应用中访问受保护资源,无需多次输入凭据。
同时,OAuth 2.0的授权机制保证了安全性,因为第三方应用无法获取用户的凭据,只能获取访问令牌来访问受保护资源。
BI系统FineBI如何实现OAuth单点登录
BI系统FineBI如何实现 OAuth单点登录OAUTH协议为用户资源的授权提供了一个安全的、开放而又简易的标准。
与以往的授权方式不同之处是OAUTH的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此OAUTH是安全的。
oAuth是Open Authorization的简写。
1.2 OAuth单点登录原理通过过滤器拦截请求,期间通过aouth的认证过程,拿到user,实现帆软的单点登录。
1.3 实现步骤1.将以下4个jave(HttpUtil.java、myexporter.java、myfilter.java、SSLConnectionClient.java)文件编译为class文件后,放到FineBI对应工程路径下:\WebReport\WEB-INF\classes\com\fr注:若FineBI对应工程路径下的WEB-INF文件夹中没有classes\com\fr该路径,自行新建该路径。
HttpUtil.java1.package com.fr;2.3.import .hsqldb.lib.StringUtil;4.5.import .ssl.*;6.import java.io.*;7.import .HttpURLConnection;8.import .URL;9.import .URLDecoder;10.import .URLEncoder;11.import java.security.SecureRandom;12.import java.security.cert.X509Certificate;13.import java.util.HashMap;14.import java.util.Map;15.import java.util.Map.Entry;16.17./**18. * 进行http访问的基本类19. */20.public class HttpUtil {21.22.private static final String DEFAULT_CHARSET = "UTF-8";23.24.private static final String METHOD_POST = "POST";25.26.private static final String METHOD_GET = "GET";27.28.private static final int CONNECTTIMEOUT = 5000;29.30.private static final int READTIMEOUT = 5000;31.32.private static class DefaultTrustManager implements X509TrustManager {33.34.public X509Certificate[] getAcceptedIssuers() {35.return null;36. }37.38.@Override39.public void checkClientTrusted(X509Certificate[] cert, String oauthType)40.throws java.security.cert.CertificateException {41. }42.43.@Override44.public void checkServerTrusted(X509Certificate[] cert, String oauthType)45.throws java.security.cert.CertificateException {46. }47. }48.49.private static HttpURLConnection getConnection(URL url, Stringmethod, String ctype)50.throws IOException {51. HttpURLConnection conn = null;52.if ("https".equals(url.getProtocol())) {53. SSLContext ctx = null;54.try {55. ctx = SSLContext.getInstance("TLS");56. ctx.init(new KeyManager[0], new TrustManager[] { new DefaultTrustManager() },57.new SecureRandom());58. } catch (Exception e) {59.throw new IOException(e);60. }61. HttpsURLConnection connHttps = (HttpsURLConnection) url.openConnection();62. connHttps.setSSLSocketFactory(ctx.getSocketFactory());63. connHttps.setHostnameVerifier(new HostnameVerifier() {64.public boolean verify(String hostname, SSLSession session) {65.return true;// 默认都认证通过66. }67. });68. conn = connHttps;69. } else {70. conn = (HttpURLConnection) url.openConnection();71. }72. conn.setRequestMethod(method);73. conn.setDoInput(true);74. conn.setDoOutput(true);75. conn.setRequestProperty("User-Agent", "quantangle-apiclient-java");76. conn.setRequestProperty("Content-Type", ctype);77. conn.setRequestProperty("Connection", "Keep-Alive");78.return conn;79. }80.81./**82. * 通过get方法访问,默认编码为utf-883. *84. * @param url 访问的url地址85. * @param params 请求需要的参数86. * @return 返回请求响应的数据87. * @throws IOException88. */89.public static String doGet(String url, Map<String, String> params) throws IOException {90.return doGet(url, params, DEFAULT_CHARSET);91. }92.93./**94. * 通过get方法访问95. *96. * @param url 访问的url地址97. * @param params 请求需要的参数98. * @param charset 字符编码99. * @return 返回请求响应的数据100. * @throws IOException101. */102.public static String doGet(String url, Map<String, String> pa rams, String charset)103.throws IOException {104.if (StringUtil.isEmpty(url) || params == null) {105.return null;106. }107. String response = "";108. url += "?" + buildQuery(params, charset);109. HttpURLConnection conn = null;110. String ctype = "application/x-www-form-urlencoded;charset=" + charset;111. conn = getConnection(new URL(url), METHOD_GET, ctype); 112. response = getResponseAsString(conn);113.return response;114. }115.116./**117. *118. * @param url api请求的权路径url地址119. * @param params api请求的业务级参数120. * @return121. * @throws IOException122. */123.public static String doPost(String url, Map<String, String> p arams) throws IOException {124.return doPost(url, params, CONNECTTIMEOUT, READTIMEOUT);125. }126.127./**128. *129. * 通过post方法请求数据,默认字符编码为utf-8130. *131. * @param url 请求的url地址132. * @param params 请求的参数133. * @param connectTimeOut 请求连接过期时间134. * @param readTimeOut 请求读取过期时间135. * @return 请求响应136. * @throws IOException137. */138.public static String doPost(String url, Map<String, String> p arams, int connectTimeOut,139.int readTimeOut) throws IOExcepti on {140.return doPost(url, params, DEFAULT_CHARSET, connectTimeOu t, readTimeOut);141. }142.143./**144. *145. * 通过post方法请求数据146. *147. * @param url 请求的url地址148. * @param params 请求的参数149. * @param charset 字符编码格式150. * @param connectTimeOut 请求连接过期时间151. * @param readTimeOut 请求读取过期时间152. * @return 请求响应153. * @throws IOException154. */155.public static String doPost(String url, Map<String, String> p arams, String charset,156.int connectTimeOut, int readTimeO ut) throws IOException {157. HttpURLConnection conn = null;158. String response = "";159. String ctype = "application/x-www-form-urlencoded;charset=" + charset;160. conn = getConnection(new URL(url), METHOD_POST, ctype); 161. conn.setConnectTimeout(connectTimeOut);162. conn.setReadTimeout(readTimeOut);163. conn.getOutputStream().write(buildQuery(params, charset).getBytes(charset));164. response = getResponseAsString(conn);165.return response;166. }167.168./**169. *170. * @param params 请求参数171. * @return 构建query172. */173.public static String buildQuery(Map<String, String> params, S tring charset) {174.if (params == null || params.isEmpty()) {175.return null;176. }177. StringBuilder sb = new StringBuilder();178.boolean first = true;179.for (Entry<String, String> entry : params.entrySet()) { 180.if (first) {181. first = false;182. } else {183. sb.append("&");184. }185. String key = entry.getKey();186. String value = entry.getValue();187.if (!StringUtil.isEmpty(key) && !StringUtil.isEmpty(v alue)) {188.try {189. sb.append(key).append("=").append(URLEncoder.encode(value, charset));190. } catch (UnsupportedEncodingException e) {} 191. }192. }193.return sb.toString();194.195. }196.197.public static Map<String, String> splitQuery(String query, St ring charset) {198. Map<String, String> ret = new HashMap<String, String>();199.if (!StringUtil.isEmpty(query)) {200. String[] splits = query.split("\\&");201.for (String split : splits) {202. String[] keyAndValue = split.split("\\=");203.boolean flag=true;204.int i=0;int len=keyAndValue.length;205.for (;i<len;i++)206. {207.if (StringUtil.isEmpty(keyAndValue[i]))208. {209. flag=false;210.break;211. }212. }213.if (flag && keyAndValue.length == 2) {214.try {215. ret.put(keyAndValue[0], URLDecoder.decode (keyAndValue[1], charset));216. } catch (UnsupportedEncodingException e) {} 217. }218. }219. }220.return ret;221. }222.223.private static byte[] getTextEntry(String fieldName, String f ieldValue, String charset)224.throws IOException {225. StringBuilder entry = new StringBuilder();226. entry.append("Content-Disposition:form-data;name=\""); 227. entry.append(fieldName);228. entry.append("\"\r\nContent-Type:text/plain\r\n\r\n"); 229. entry.append(fieldValue);230.return entry.toString().getBytes(charset);231. }232.233.private static byte[] getFileEntry(String fieldName, String f ileName, String mimeType,234. String charset) throws IOE xception {235. StringBuilder entry = new StringBuilder();236. entry.append("Content-Disposition:form-data;name=\""); 237. entry.append(fieldName);238. entry.append("\";filename=\"");239. entry.append(fileName);240. entry.append("\"\r\nContent-Type:");241. entry.append(mimeType);242. entry.append("\r\n\r\n");243.return entry.toString().getBytes(charset);244. }245.246.private static String getResponseAsString(HttpURLConnection c onn) throws IOException {247. String charset = getResponseCharset(conn.getContentType() );248. InputStream es = conn.getErrorStream();249.if (es == null) {250.return getStreamAsString(conn.getInputStream(), chars et);251. } else {252. String msg = getStreamAsString(es, charset);253.if (StringUtil.isEmpty(msg)) {254.throw new IOException("{\"" + conn.getResponseCod e() + "\":\"" + conn.getResponseMessage() + "\"}");255. } else {256.throw new IOException(msg);257. }258. }259.260. }261.262.private static String getStreamAsString(InputStream input, St ring charset) throws IOException {263. StringBuilder sb = new StringBuilder();264. BufferedReader bf = null;265.try {266. bf = new BufferedReader(new InputStreamReader(input, charset));267. String str;268.while ((str = bf.readLine()) != null) {269. sb.append(str);270. }271.return sb.toString();272. } finally {273.if (bf != null) {274. bf.close();275. bf = null;276. }277. }278.279. }280.281.private static String getResponseCharset(String ctype) { 282. String charset = DEFAULT_CHARSET;283.if (!StringUtil.isEmpty(ctype)) {284. String[] params = ctype.split("\\;");285.for (String param : params) {286. param = param.trim();287.if (param.startsWith("charset")) {288. String[] pair = param.split("\\=");289.if (pair.length == 2) {290. charset = pair[1].trim();291. }292. }293. }294. }295.return charset;296. }297.298.}myexporter.java1.package com.fr;2.import com.fr.base.DynamicUnitList;3.import com.fr.base.FRContext;4.import com.fr.base.ResultFormula;5.import paratorUtils;6.import com.fr.general.DeclareRecordType;7.import com.fr.general.ModuleContext;8.import com.fr.general.web.ParameterConsts;9.import com.fr.io.TemplateWorkBookIO;10.import com.fr.io.exporter.AppExporter;11.import com.fr.io.exporter.PDFExporter;12.import com.fr.io.exporter.WordExporter;13.import com.fr.json.JSONArray;14.import com.fr.json.JSONException;15.import com.fr.json.JSONObject;16.import com.fr.main.TemplateWorkBook;17.import com.fr.main.workbook.ResultWorkBook;18.import com.fr.page.PageSetProvider;19.import com.fr.page.PaperSettingProvider;20.import com.fr.report.cell.CellElement;21.import com.fr.report.cell.DefaultTemplateCellElement;22.import com.fr.report.cell.ResultCellElement;23.import com.fr.report.cell.cellattr.PageExportCellElement;24.import com.fr.report.core.ReportUtils;25.import com.fr.report.core.block.PolyResultWorkSheet;26.import com.fr.report.module.EngineModule;27.import com.fr.report.worksheet.PageRWorkSheet;28.import com.fr.stable.CodeUtils;29.import com.fr.stable.ColumnRow;30.import com.fr.stable.PageActor;31.import com.fr.stable.unit.FU;32.import com.fr.stable.unit.UNIT;33.import com.fr.web.Browser;34.import com.fr.web.core.ErrorHandlerHelper;35.import com.fr.web.core.utils.ExportUtils;36.import com.fr.web.utils.WebUtils;37.import javax.servlet.ServletException;38.import javax.servlet.http.HttpServlet;39.import javax.servlet.http.HttpServletRequest;40.import javax.servlet.http.HttpServletResponse;41.import java.io.IOException;42.import java.io.OutputStream;43.import java.util.*;44.import java.util.regex.Matcher;45.import java.util.regex.Pattern;46.public class myexporter extends HttpServlet {47.private static boolean offlineWriteAble = true;48.private static final Pattern KEY_VALUE_PATTERN = pile("[^{,}]*:[^{,}]*");49.public static void dealResponse4Export(HttpServletResponse res){50. res.setHeader("Cache-Control", "public");51. res.setHeader("Cache-Control", "max-age=3");52. res.reset();53. }54.private static final long serialVersionUID = 1L;55.public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {56. doGet(request, response);57. }58.public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {59.// 首先需要定义执行所在的环境,这样才能正确读取数据库信息60.try {61. ModuleContext.startModule(EngineModule.class.getName());62. dealResponse4Export(res);63. String fileName = WebUtils.getHTTPRequestParameter(req, ParameterConsts.__FILENAME__);64. String format = WebUtils.getHTTPRequestParameter(req, "format");65. Browser browser = Browser.resolve(req);66. fileName = browser.getEncodedFileName4Download(fileName);67. List paraMapList = new ArrayList();68. List reportPathList = new ArrayList();69. PaperSettingProvider paperSettingProvider=null;70.if (WebUtils.getHTTPRequestParameter(req, ParameterConsts.REPORTLETS) != null) {71. createReportsFromReportlets(WebUtils.getHTTPRequestParameter(req, ParameterConsts.REPORTLETS), reportPathList, par aMapList);72. ResultWorkBook[] resultWorkBook = new ResultWorkBook[reportPathList.size()];73. PolyResultWorkSheet allInOneSheet = new PageRWorkSheet();74.for (int i = 0; i < reportPathList.size(); i++){75. TemplateWorkBook workbook = TemplateWorkBookIO.readTemplateWorkBook(FRContext.getCurrentEnv(), String.valueOf( reportPathList.get(i)));76. java.util.Map paraMap = (Map) paraMapList.get(i);77. resultWorkBook[i] = workbook.execute(paraMap, new PageActor());78.if (i==0)79. {80. paperSettingProvider=ReportUtils.getPaperSettingListFromWorkBook(workbook).get(0);81. }82. }83.int countx; int county; int length = resultWorkBook.length;84.long sumx;long sumy;85.long[] lengthx = new long[length];long[] lengthy = lengthx.clone();86.if (length > 0) {87. lengthx[0] = lengthy[0] = 0;88. }89.for (int i = 1; i < length; i++) {90. sumy = 0;91. county = resultWorkBook[i - 1].getElementCaseReport(0).getRowCount();92.while (county-- > 0) {93. sumy = sumy + resultWorkBook[i - 1].getElementCaseReport(0).getRowHeight(county).getLen();94. }95.96. lengthx[i] = 0;lengthy[i] = sumy+lengthy[i-1];97. }98. ArrayList<UNIT> verticalList = new ArrayList<UNIT>();99. ArrayList<UNIT> horizontalList = new ArrayList<UNIT>();100. analyElementColumnRow(verticalList, horiz ontalList, resultWorkBook, lengthx, lengthy);101. allInOneSheet = setNewColRowSize(vertical List, horizontalList, allInOneSheet);102.//填充空白单元格103. allInOneSheet = fillBlankCell(verticalLis t.size(), horizontalList.size(), allInOneSheet);104.for (int i = 0, len = reportPathList.size (); i < len; i++) {105.//添加当前元素到新sheet中去106. allInOneSheet = addElemToSheet(vertic alList, horizontalList, allInOneSheet, resultWorkBook[i], lengthx[i ], lengthy[i]);107. }108.if (paperSettingProvider==null)109. {110.return;111. }112.// 将结果工作薄导出为Excel文件113. doExport(req, res, format, fileName,false, br owser,allInOneSheet.generateReportPageSet(paperSettingProvider)); 114. }115. }catch(Exception e){116. e.printStackTrace();117. }118. }119.private static void doExport(HttpServletRequest req, HttpServ letResponse res,String format, String fileName, boolean isEmbbed, B rowser browser,PageSetProvider page) throws Exception {120. AppExporter[] exporters = new AppExporter[] {null}; 121. DeclareRecordType[] exportTypes = new DeclareRecordType[] {null};122. OutputStream outputStream = res.getOutputStream();123. getExporterAndTypeAndexport(req, res, format, fileName, i sEmbbed, browser, exporters,exportTypes,outputStream,page);124. DeclareRecordType exportType = exportTypes[0];125.126.if (exportType == null) {127. ErrorHandlerHelper.getErrorHandler().error(req, res, "Cannot recognize the specifed export format:" + format + ",\nThe c orrect format can be PDF,Excel,Word,SVG,CSV,Text or Image."); 128.return;129. }130.131.try {132. outputStream.flush();133. outputStream.close();134. } catch (IOException e) {135.// alex:有些Exporter在export里面可能会已经做了out.close 操作,为了不打印IOException,这里catch到之后不输出136. }137. }138.private static void getExporterAndTypeAndexport(HttpServletRe quest req, HttpServletResponse res,139. String format, String fileName, boolean isEmbbed, Browser browser, AppExporter[] exporter s, DeclareRecordType[] exportTypes,OutputStream out,PageSetProvider page) throws Exception{140.if (format.equalsIgnoreCase("PDF")) {141. ExportUtils.setPDFContent(res, fileName, isEmbbed); 142. PDFExporter PDFExport = new PDFExporter();143. PDFExport.export(out, page);144. } else if (format.equalsIgnoreCase("Word")) {145. ExportUtils.setWordConetent(res, fileName);146. WordExporter WordExport = new WordExporter();147. WordExport.export(out, page);148. }149. }150.// private static AppExporter getPDFExporter(HttpServletReques t req, ReportSessionIDInfor sessionIDInfor) {151.// AppExporter exporter;152.// String extype = WebUtils.getHTTPRequestParameter(req, "extype");153.// // james:是否是PDF客户端打印154.// boolean isPDFPrint = ComparatorUtils.equals(WebUtils.ge tHTTPRequestParameter(req, "isPDFPrint"), "true");155.// // 标志是否正在为客户端PDF打印而生成PDF文件156.// // 打印的话,记录下157.// if (isPDFPrint) {158.// LogUtils.recordPrintInfo(sessionIDInfor.getBookPath (), sessionIDInfor.getParameterMap4Execute4Consisent(),159.// DeclareRecordType.PRINT_TYPE_PDF, sessionID Infor);160.// if (!Browser.resolve(req).isIE()) {161.// FRContext.getLogger().error(Inter.getLocText("N S_print_pdf"));162.// }163.// }164.// /*165.// * 根据sessionIDInfor获取当前的打印机偏移量,若为打印则设置偏移导出则不偏移166.// */167.// SetPrinterOffsetService setprinteroffsetservice = new S etPrinterOffsetService();168.// float[] offset = setprinteroffsetservice.getOffsetBySes sionIDInfor(sessionIDInfor);169.// sessionIDInfor.setoffset(isPDFPrint ? offset : new floa t[2]);170.// // carl:两种类型171.// if (ComparatorUtils.equals(ExportConstants.TYPE_RESOLVE SOMETHING, extype)) {172.// exporter = new PDFExporter2(isPDFPrint);173.// } else {174.// exporter = new PDFExporter(isPDFPrint);175.// }176.// return exporter;177.// }178.//179.// private static void dealExcelExporter(HttpServletRequest re q, HttpServletResponse res, ReportSessionIDInfor sessionIDInfor, 180.// AppExporter[] exporte rs, DeclareRecordType[] exportTypes, String fileName, Browser brows er) {181.// // carl:四种输出方式182.// String extype = WebUtils.getHTTPRequestParameter(req, "extype");183.// // 假如是层式报表默认使用大数据方式184.// ResultWorkBook wb = sessionIDInfor.getWorkBook2Show();185.// boolean isPage = true; // 大数据量的时候要不要分页186.// for (int i = 0; i < wb.getReportCount(); i++) {187.// if (wb.getReport(i) instanceof LayerReport || wb.ge tReport(i) instanceof LayerPageReport) {e(extype)) {189.// isPage = false;190.// }191.// if (!ExportConstants.TYPE_PAGETOSHETT.equalsIgn oreCase(extype)) {192.// extype = ExportConstants.TYPE_LARGEDATA_PAG E;193.// }194.// break;195.// }196.// }197.//198.// if (ExportConstants.TYPE_LARGEDATA_PAGE.equalsIgnoreCas e(extype)) {199.// ExportUtils.setZipContext(res, fileName, browser.sh ouldSetContentTypeOnZipDownload());200.// exporters[0] = new LargeDataPageExcelExporter(Repor tUtils.getPaperSettingListFromWorkBook(sessionIDInfor.getContextBoo k()), isPage);201.// exportTypes[0] = DeclareRecordType.EXPORT_TYPE_EXCE L_LARGE;202.// } else {203.// if (ExcelUtils.checkPOIJarExist() && !WebUtils.getH TTPRequestBoolParameter(req, "isExcel2003")) {204.// ExportUtils.setExcel2007Content(res, fileName);205.// } else {206.// ExportUtils.setExcelContent(res, fileName); 207.// }208.//209.// if (ExportConstants.TYPE_SIMPLE.equalsIgnoreCase(ex type)) {210.// exporters[0] = new ExcelExporter(ReportUtils.ge tPaperSettingListFromWorkBook(sessionIDInfor.getContextBook())); 211.// exportTypes[0] = DeclareRecordType.EXPORT_TYPE_ EXCEL_ORIGINAL;212.// } else if (ExportConstants.TYPE_PAGETOSHETT.equalsI gnoreCase(extype)) {213.// exporters[0] = new PageToSheetExcelExporter(Rep ortUtils.getPaperSettingListFromWorkBook(sessionIDInfor.getContextB ook()));214.// exportTypes[0] = DeclareRecordType.EXPORT_TYPE_ EXCEL_PAGESHEET;Case(extype)) {216.// exporters[0] = new StreamExcelExporter(ReportUt ils.getPaperSettingListFromWorkBook(sessionIDInfor.getContextBook() ));217.// exportTypes[0] = DeclareRecordType.EXPORT_TYPE_ EXCEL_ORIGINAL;218.// } else {219.// exporters[0] = new PageExcelExporter(ReportUtil s.getPaperSettingListFromWorkBook(sessionIDInfor.getContextBook())) ;220.// exportTypes[0] = DeclareRecordType.EXPORT_TYPE_ EXCEL_PAGE;221.// }222.//223.// ((ExcelExporter)exporters[0]).setExcel2003(WebUtils .getHTTPRequestBoolParameter(req, "isExcel2003"));224.// }225.// }226.//227.// private static DeclareRecordType getImageExportType(HttpSer vletRequest req) {228.// DeclareRecordType[] supportImageType = {DeclareRecordTy pe.EXPORT_TYPE_IMAGE_PNG, DeclareRecordType.EXPORT_TYPE_IMAGE_JPG,229.// DeclareRecordType.EXPORT_TYPE_IMAGE_GIF, Declar eRecordType.EXPORT_TYPE_IMAGE_BMP, DeclareRecordType.EXPORT_TYPE_IM AGE_WBMP230.// };231.// String extype = WebUtils.getHTTPRequestParameter(req, "extype");232.// int defaulttype = 0;233.// if (extype != null) {234.// for (int i = 0; i < supportImageType.length; i++) {235.// if (extype.equalsIgnoreCase(supportImageType[i] .getTypeString())) {236.// defaulttype = i;237.// break;238.// }239.// }240.// }241.// return supportImageType[defaulttype];242.// }243.public static PolyResultWorkSheet fillBlankCell(int rowCount, int colCount,PolyResultWorkSheet allInOneSheet ){244.for (int i = 0; i < rowCount; i++) {245.for (int j = 0; j < colCount; j++) {246.//填充空白格, 主要是为了方便后续分页取cell iterator.next方便, 不然是null就不往下走了247. ResultCellElement ce = createDefaultCellElement(j , i);248. allInOneSheet.addCellElement(ce);249. }250. }251. allInOneSheet.setRowMappingArray(new int[0]);252. allInOneSheet.setColumnMappingArray(new int[0]);253.return allInOneSheet;254. }255.public static ResultCellElement createDefaultCellElement(int col, int row){256.return new PageExportCellElement(new DefaultTemplateCellE lement(col, row));257. }258.public static PolyResultWorkSheet addElemToSheet(ArrayList< UNIT> verticalList, ArrayList<UNIT> horizontalList, PolyResultWorkS heet page_sheet, ResultWorkBook resultWorkBook,long lengthx,long l engthy){259. UNIT x= FU.getInstance(lengthx);260. UNIT y= FU.getInstance(lengthy);261. DynamicUnitList newRowList = page_sheet.getRowHeightList_ DEC();262. DynamicUnitList newColList = page_sheet.getColumnWidthLis t_DEC();263.int rowCount = page_sheet.getRowCount();264.int colCount = page_sheet.getColumnCount();265. DynamicUnitList rowHeightList = resultWorkBook.getElement CaseReport(0).getRowHeightList_DEC();266. DynamicUnitList colWidthList = resultWorkBook.getElementC aseReport(0).getColumnWidthList_DEC();267. Iterator<CellElement> it = resultWorkBook.getElementCase Report(0).cellIterator();268. HashMap<String, String> columnRowMap = new HashMap<String , String>();269. HashMap<CellElement, ResultFormula> formulaMap = new Hash Map<CellElement, ResultFormula>();270.while(it.hasNext()){271. CellElement ce = it.next();272. UNIT ceX = x.add(colWidthList.getRangeValueFromZero(ce.getColumn()));273. UNIT ceWidth = colWidthList.getRangeValue(ce.getColum n(), ce.getColumn() + ce.getColumnSpan());274. UNIT ceY = y.add(rowHeightList.getRangeValueFromZero( ce.getRow()));275. UNIT ceHeight = rowHeightList.getRangeValue(ce.getRow (), ce.getRow() + ce.getRowSpan());276.//直接去坐标列表里找, 然后+1, 因为行高list就是根据坐标列表生成的.277.int newCeCol = horizontalList.indexOf(ceX) + 1; 278.int newCeColSpan = getNewSpan(newCeCol, newColList, c eWidth, colCount);279.int newCeRow = verticalList.indexOf(ceY) + 1;280.int newCeRowSpan = getNewSpan(newCeRow, newRowList, c eHeight, rowCount);281. ColumnRow oriCR = ColumnRow.valueOf(ce.getColumn(), ce.getRow());282. ColumnRow newCR = ColumnRow.valueOf(newCeCol, newCeRo w);283.//记录下格子的变迁, A1->B5, 这样后续公式有用到A1的都改成B5284. columnRowMap.put(oriCR.toString(), newCR.toString());285. ResultCellElement newCe = (ResultCellElement) ce.deri veCellElement(newCeCol, newCeRow, newCeColSpan, newCeRowSpan); 286. page_sheet.addCellElement(newCe);287.if(ce.getValue() instanceof ResultFormula){288. formulaMap.put(newCe, (ResultFormula) ce.getValue ());289. }290. }291.//改变所有保留公式里的ColumnRow292. modifyAllFormula(formulaMap, columnRowMap);293.return page_sheet;294. }295.public static int getNewSpan(int newCeColRow, DynamicUnitList newColRowList, UNIT ceWidthHeight, int count){296.for (int i = newCeColRow; i < count + 1; i++) {297.if(ComparatorUtils.equals(ceWidthHeight, newColRowLis t.getRangeValue(newCeColRow, i))){298.return i - newCeColRow;299. }300. }301.302.return0;303. }304.public static PolyResultWorkSheet setNewColRowSize(ArrayL ist<UNIT> verticalList, ArrayList<UNIT> horizontalList, PolyResu ltWorkSheet allInOneSheet){305.//从坐标列表里生成行高列宽 [0, 2, 6] -> [2, 4]306.for (int i = 0; i < verticalList.size(); i++) {307. UNIT lastCoordinate = i == 0 ? UNIT.ZERO : verticalLi st.get(i - 1);308. UNIT rowHeight = verticalList.get(i).subtract(lastCoo rdinate);309. allInOneSheet.setRowHeight(i, rowHeight);310. }311.312.for (int i = 0; i < horizontalList.size(); i++) {313. UNIT lastCoordinate = i == 0 ? UNIT.ZERO : horizontal List.get(i - 1);314. UNIT colWidth = horizontalList.get(i).subtract(lastCo ordinate);315. allInOneSheet.setColumnWidth(i, colWidth);316. }317.return allInOneSheet;318. }319.public static void analyElementColumnRow(ArrayList<UNIT> vert icalList, ArrayList<UNIT> horizontalList,320. ResultWorkBook [] re sultWorkBooks,long [] lengthx,long [] lengthy){321.int length=resultWorkBooks.length;322.for (int i=0;i<length;i++) {323. ResultWorkBook resultWorkBook = resultWorkBooks[i]; 324. UNIT y = FU.getInstance(lengthy[i]);325.int rowCount = resultWorkBook.getElementCaseReport(0) .getRowCount();326. DynamicUnitList rowHeightList = resultWorkBook.getEle mentCaseReport(0).getRowHeightList_DEC();327. analyColumnRow(y, verticalList, rowHeightList, rowCou nt);328.329.//列处理330. UNIT x = FU.getInstance(lengthx[i]);331.int colCount = resultWorkBook.getElementCaseReport(0) .getColumnCount();。
统一身份认证系统的设计与实现
统一身份认证系统的设计与实现随着互联网的快速发展和普及应用,人们对于网上服务的需求也越来越高。
无论是网上购物、在线银行还是社交媒体,这些服务都要求用户进行身份认证,以确保用户信息的安全性和服务的可信度。
为了解决这个问题,统一身份认证系统应运而生。
统一身份认证系统是一种集中管理和授权用户身份的系统,其核心目标是实现用户在多个应用中使用同一个身份标识进行认证和授权。
这样用户只需要一次认证,便可获得对多个应用的访问权限,提高了用户的便利性和服务的效率。
设计和实现一个好的统一身份认证系统涉及到多个方面的考虑和技术的应用。
下面将从以下几个方面介绍。
首先,安全性是统一身份认证系统设计的重中之重。
用户信息的安全性是用户选择使用该系统的最基本的保障。
设计者需要使用最先进的加密算法和安全协议来保护用户的个人信息,以防止用户信息被盗用或泄露。
其次,系统的可扩展性也是一个重要的考虑因素。
随着用户数量和业务规模的增长,系统需要能够处理大规模的身份认证请求。
可扩展性的设计可以包括将系统划分为多个分布式节点,采用负载均衡和故障恢复机制来提高系统的稳定性和可用性。
另外,用户体验也是统一身份认证系统设计的关键。
用户在登录和认证过程中,如果体验不好,可能会降低用户使用该系统的积极性。
因此,设计者需要考虑简化认证流程、增加多种认证方式和提供忘记密码等用户友好的功能。
除了以上方面,统一身份认证系统还需要和其他系统进行无缝集成。
这意味着系统需要支持各种不同的协议和接口,以实现与不同应用系统之间的数据交互和认证授权的传递。
例如,系统可以支持OAuth和SAML等标准协议,以适应不同应用的要求。
对于统一身份认证系统的实施,需要一定的技术支持。
开发团队应具备丰富的安全和身份认证技术的知识,熟悉常用的身份认证协议和加密算法。
同时,合理的项目管理和团队协作也是保证项目能够按时交付和高质量实现的重要因素。
总结起来,统一身份认证系统的设计与实现是一个复杂而又关键的任务。
前端开发知识:使用JWT和OAuth来实现跨域认证和授权
前端开发知识:使用JWT和OAuth来实现跨域认证和授权前言随着互联网的快速发展,越来越多的应用和服务都采用了分布式架构。
在这种情况下,不同的服务器之间需要进行跨域认证和授权,以保障应用的安全性和可用性。
而JWT和OAuth正是现代应用中常用的跨域认证和授权方案。
本文将从基础概念入手,介绍JWT和OAuth的定义和工作原理,并分别对其在实际应用中的应用场景和优缺点进行了分析和总结。
JWTJWT全称为JSON Web Token,是一种基于JSON格式的轻量级开放标准。
它可以在用户和网站之间传递安全可靠的信息。
简单来说,JWT 是由三部分组成的字符串,分别是头部信息、载荷信息和签名信息。
头部信息中包含了算法信息,载荷信息中包含了需要传递的信息,签名信息则用于保护数据的完整性。
JWT的工作原理如下:1.在用户登录成功后,服务器生成一个JWT;2.服务器将JWT发送给客户端,客户端将其保存在cookie或localStorage中;3.当客户端向服务器发起请求时,客户端将JWT包含在请求头中;4.服务器验证JWT的签名信息,如果验证通过,则返回请求数据。
JWT的优点有:1.可以在不同的服务器之间共享用户信息,而不需要将用户识别数据存储在本地;2. JWT可以包含用户身份验证(例如用户ID),因此不需要每次请求都到数据库中检索数据,减少了服务器负载。
OAuthOAuth是一种开放标准,用于管理用户授权。
在OAuth中,用户可以授权第三方应用程序访问他们存储在另一个服务提供商上的资源。
OAuth包含了四个角色:资源拥有者、客户端、认证服务器和资源服务器。
OAuth的工作原理如下:1.用户向客户端提供自己的登录凭证(例如用户名和密码);2.客户端使用这些凭证向认证服务器发出请求,从而获取访问令牌;3.客户端使用这个访问令牌向资源服务器发出请求,从而获取资源;4.客户端使用刷新令牌,如果需要,从认证服务器重新获取访问令牌。
java 单点登录实现方案
java 单点登录实现方案Java单点登录(Single Sign-On,简称SSO)是一种身份验证机制,允许用户使用一组凭据(如用户名和密码)登录到多个相关的应用程序或系统,而无需在每个应用程序中单独进行身份验证。
本文将介绍Java单点登录的实现方案。
在实现Java单点登录时,可以采用以下方案:1. 基于Token的认证方案:这是目前较为常见的单点登录实现方案之一。
用户在登录成功后,后台生成一个Token,将其返回给用户,并存储在服务器端。
用户在访问其他应用程序时,将Token作为身份凭证发送到服务器端进行验证。
服务器端通过验证Token的有效性来判断用户的身份。
常见的Token生成方式包括JWT(JSON Web Token)和OAuth2.0。
2. 基于代理的认证方案:该方案使用一个代理服务器来处理用户的身份验证。
用户在登录成功后,代理服务器会为用户生成一个身份标识,并将其存储在Cookie中。
当用户访问其他应用程序时,请求会先发送到代理服务器,代理服务器会验证用户的身份,并将请求转发给相应的应用程序。
3. 基于Session的认证方案:该方案使用Java的Session机制来实现单点登录。
用户在登录成功后,后台会为用户创建一个Session,并将Session的ID存储在Cookie中。
当用户访问其他应用程序时,应用程序会通过Cookie中的Session ID来验证用户的身份。
无论采用哪种方案,Java单点登录的实现步骤大致相同:1. 用户登录:用户在登录页面输入用户名和密码进行登录。
后台验证用户的凭据是否正确,如果正确则进行下一步操作。
2. 生成身份凭证:在用户登录成功后,后台会生成一个身份凭证(Token、身份标识或Session),并将其返回给用户。
3. 存储身份凭证:服务器端会将生成的身份凭证存储起来,可以存储在数据库、缓存或服务器内存中。
4. 跨应用程序身份验证:当用户访问其他应用程序时,应用程序会验证用户的身份凭证的有效性。
qq登录实现原理
qq登录实现原理QQ登录实现原理主要涉及到OAuth 2.0协议的使用。
OAuth (开放授权)是一种允许第三方应用访问用户在某个服务提供商上的资源的授权协议。
用户在第三方应用中选择使用QQ登录时,第三方应用会引导用户访问QQ登录页面。
用户输入QQ账号和密码之后,第三方应用会向QQ登录服务器发起登录请求。
QQ登录服务器根据用户的登录信息验证用户身份的准确性,并生成一个授权码。
授权码的生成是由QQ登录服务器根据应用注册时分配的App ID和App Key进行生成和计算的。
QQ登录服务器将授权码返回给第三方应用服务器。
第三方应用服务器收到授权码后,需要使用申请时分配的App ID、App Key以及授权码向QQ登录服务器发起获取Access Token的请求。
QQ登录服务器接收到该请求后,会进行身份验证和授权码的核对。
通过验证后,QQ登录服务器会向第三方应用服务器返回一个Access Token。
Access Token是用于标识用户身份和访问权限的令牌,具有一定的时效性。
第三方应用服务器在获取到Access Token后,可以使用该令牌向QQ服务器请求获取用户的基本信息,如昵称、头像等。
这个请求需要使用Access Token作为身份验证,并按照QQ登录服务器提供的API接口进行调用。
QQ服务器接收到请求后,验证Access Token的有效性,并返回相应的用户信息给第三方应用服务器。
第三方应用服务器可以使用获取到的用户信息完成用户的登录、注册或其他操作。
值得注意的是,为了保证安全性,第三方应用在使用QQ登录时需要进行OAuth 2.0的签名验证和请求参数加密等操作,以防止信息泄露和非法请求的风险。
同时,QQ登录服务器也会对登录行为进行监测和管理,防止恶意攻击和用户信息泄露的发生。
OAuth2.0认证流程是如何实现的?
OAuth2.0认证流程是如何实现的?导读⼤家也许都有过这样的体验,我们登录⼀些不是特别常⽤的软件或⽹站的时候可以使⽤QQ、微信或者微博等账号进⾏授权登陆。
例如我们登陆⾖瓣⽹的时候,如果不想单独注册⾖瓣⽹账号的话,就可以选择⽤微博或者微信账号进⾏授权登录。
这样的场景还有很多,例如登录微博、头条等⽹站,也都可以选择QQ或者微信登录的⽅式。
那么这样的第三⽅登陆⽅式到底是怎么实现的呢?难道是腾讯把我们QQ或者微信的账户信息卖给了这些⽹站不成?很显然,腾讯是不会这么⼲的,⽽这种登录⽅式的实现就是我们这篇⽂章中要给⼤家介绍的OAuth2.0的认证⽅式。
类似地,在公司内部,如果公司有多套不同的软件系统,例如我们⽇常使⽤的GitLab代码管理、公司内⽹系统、财务报销系统以及很多业务系统的管理后台,是不是也可以实现⼀个员⼯账号就能授权访问所有以上这些系统,⽽不需要每个系统都开通单独的账号,设置独⽴的密码才⾏呢?实际上在很多公司都是可以实现这种效果的,这也就是我们通常所说的SSO 单点登录系统。
在接下来的内容中,码农哥会先给⼤家具体介绍下OAuth2.0的基本原理,然后再通过Spring Boot实现⼀套遵循OAuth2.0规范的SSO单点登录系统!什么是OAuth2.0?OAuth2.0是⼀种允许第三⽅应⽤程序使⽤资源所有者的凭据获得对资源有限访问权限的⼀种授权协议。
例如在前⾯的例⼦中,通过微信登录⾖瓣⽹的过程,就相当于微信允许⾖瓣⽹作为第三⽅应⽤程序在经过微信⽤户授权后,通过微信颁发的授权凭证有限地访问⽤户的微信头像、⼿机号,性别等受限制的资源,从⽽来构建⾃⾝的登录逻辑。
需要说明的是在OAuth2.0协议中第三⽅应⽤程序获取的凭证并不等同于资源拥有者持有的⽤户名和密码,以上⾯例⼦来说微信是不会直接将⽤户的⽤户名、密码等信息作为凭证返回给⾖瓣的。
这种授权访问凭证⼀般来说就是⼀个表⽰特定范围、⽣存周期和其访问权限的⼀个由字符串组成的访问令牌,也就是我们常说的token。
实现OAUTH协议实现QQ第三方登录效果
实现OAUTH协议实现QQ第三⽅登录效果1.OAuth的简述OAuth(Open Authorization,开放授权)是为⽤户资源的授权定义了⼀个安全、开放及简单的标准,第三⽅⽆需知道⽤户的账号及密码,就可获取到⽤户的授权信息,并且这是安全的。
1.主要的应⽤场景1.⽬前很多⽹站集成了新浪微博,QQ等登录平台,这带来的好处是不⾔⽽喻的,只需要⼀个QQ号,就可以登录集成了qq登录的⽹站,不⽤记住太多的⽤户名和密码,如果QQ能够⼀统江湖,对于⽤户将是⼤⼤的好事。
2.有时候希望访问授权平台的资源,也可以通过这样⽅式实现。
2.运⾏原理步骤如下:1.⽤户访问第三⽅应⽤。
2.访问授权⽅如QQ的授权界⾯。
3.⽤户在QQ登录界⾯上输⼊⽤户名密码,进⾏授权。
4.第三⽅应⽤获取授权信息。
在这个过程中,登录验证的操作都在授权⽅进⾏。
如qq。
2.项⽬中遇到的问题在客户端调⽤webservice的时候,我们⽐如需要获取某个⽤户的个⼈资料信息,我们会这样使⽤:String getInfoByAccount(String account);我们会传⼊帐号,这样就会带来⼀个问题,只要知道某⼈的帐号,别有⽤⼼的⽤户就会够着别⼈的帐号,获取别⼈的隐秘信息。
我想可以通过⾃⼰实现OAUTH的⽅式来⾃⼰实现⼀个。
实现的过程如下:1.授权在第三⽅⽹站,点击授权⽅的登录界⾯,这个界⾯在授权服务器上。
2.输⼊⽤户名密码后,如果登录成功,将⽣成的token 跳转到第三⽅的验证程序上。
3.第三⽅的验证程序将token提交到授权端进⾏验证,验证成功后,则返回⽤户信息和token。
4.第三⽅程序将⽤户和token写⼊到session中。
5.写⼊后则代表第三⽅应⽤登录成功。
授权⽅代码实现:1.登录成功后,将当前⽤户写⼊到⼀个缓存中,缓存的key,是刚⽣成的随机token,value 值为IAuthUser。
2. 授权⽅对token进⾏验证。
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String id=request.getParameter("uid");response.setContentType("text/json;charset=utf-8");IAuthService service=AppUtil.getBean(IAuthService.class);IAuthUser authuser= service.getByUid(id);if(authuser==null){response.getWriter().print("{result:-1,msg:\"userNotFound\"}");}else{boolean timeOut=authuser.isTimeOut();if(timeOut){response.getWriter().print("{result:-2,msg:\"timeout\"}");}else{IUser user=authuser.getUser();service.setAuth(user);String json="{result:0,id:\""+user.getUserId()+"\",account:\""+user.getAccount()+"\",fullname:\""+user.getFullname()+"\"}";response.getWriter().print(json);}}}3.第三⽅应⽤验证逻辑。
单点登录实现方案
单点登录实现方案简介单点登录(Single Sign-On,简称 SSO)是一种允许用户使用一组凭据(例如用户名和密码)登录多个相关但独立的应用程序或系统的身份认证机制。
在传统的身份验证方式中,用户需要为每个应用程序或系统输入不同的身份验证信息,这导致了用户体验的不便和管理的困难。
通过实现单点登录,用户只需要在一处登录后即可访问所有相关的应用程序或系统,提高了用户的便利性和工作效率。
本文将介绍一种基于令牌(Token)的单点登录实现方案,采用 OAuth 2.0 和OpenID Connect 协议来实现身份认证与授权。
方案概述1.用户通过认证服务器进行身份认证。
2.认证服务器颁发令牌给用户。
3.用户携带令牌访问相关应用程序或系统。
OAuth 2.0OAuth 2.0 是一种开放授权协议,用于授权第三方应用程序访问用户在某个服务提供商上存储的资源。
在单点登录中,OAuth 2.0 充当了认证服务器的角色。
认证服务器获取用户的用户名和密码,验证其身份后,颁发访问令牌给用户。
访问令牌包含了用户授权的权限信息,并具有一定的有效期。
OpenID ConnectOpenID Connect 是建立在 OAuth 2.0 协议之上的身份验证协议,提供了用于身份验证和用户信息获取的规范。
在单点登录中,OpenID Connect 充当了身份提供商的角色。
一旦用户通过 OAuth 2.0 的流程进行身份认证并获得访问令牌,OpenID Connect 标识提供者将根据令牌中携带的用户ID等信息,向应用程序或系统提供有关用户的身份验证和授权信息。
单点登录流程图graph TDA[用户] -->|1. 请求登录| B[认证服务器]B -->|2. 请求授权| C[用户同意授权]C -->|3. 颁发令牌| BB -->|4. 颁发令牌| AA -->|5. 携带令牌| D[应用程序/系统]D -->|6. 验证令牌| BB -->|7. 验证令牌| D具体实现1. 配置认证服务器首先,我们需要配置一个认证服务器,用于处理用户的身份认证请求。
移动应用开发中的第三方登录与授权实现方法
移动应用开发中的第三方登录与授权实现方法人们现如今几乎每天都与各种类型的移动应用打交道,如社交媒体、电子商务、游戏等。
为了方便用户的注册和登录流程,许多应用采用了第三方登录与授权功能。
这种功能能够让用户使用自己已经拥有的社交媒体账号(如微信、微博、Facebook 等)进行快速登录,并共享一定的用户信息。
在本文中,我们将讨论移动应用开发中的第三方登录与授权实现方法,以及与之相关的安全隐私问题。
第三方登录与授权的实现方法有多种,但最常见的是使用OAuth协议。
OAuth协议是一个开放标准的授权协议,它允许用户授权第三方应用访问其在认证服务器上存储的受限资源。
在移动应用中,OAuth协议通过调用第三方授权接口实现。
接下来,我们将详细介绍OAuth协议的实现步骤。
首先,开发者需要在自己的应用后台注册第三方登录的应用程序。
注册成功后,开发者将获得一个客户端ID和密钥用于后续的交互。
接着,开发者需要在移动应用的登录页面中添加第三方登录按钮,并根据需求选择要支持的第三方平台。
当用户点击第三方登录按钮后,移动应用将会跳转到对应的第三方登录页面。
用户需要输入自己的账号和密码进行登录。
登录成功后,第三方平台将会生成一个授权码并返回给移动应用。
移动应用拿到授权码后,将会使用客户端ID和密钥等参数发送一个请求到认证服务器,请求获取访问令牌。
认证服务器将会验证客户端ID和密钥的合法性,并根据用户的授权码颁发访问令牌。
移动应用在获取到访问令牌后,将可以使用该令牌访问用户的个人信息。
在整个过程中,开发者需要考虑一些安全隐私问题。
首先,开发者要确保客户端ID和密钥的安全,避免泄露给未经授权的人员。
其次,在用户同意授权之前,开发者不得查询、存储或分享用户的个人信息。
最后,开发者要及时更新第三方登录的API,以修复可能存在的漏洞,确保用户账号的安全。
除了OAuth协议,还有其他第三方登录与授权的实现方法。
例如,一些社交媒体平台提供了SDK供开发者使用。
移动应用中安全登录和用户认证的实现方法
移动应用中安全登录和用户认证的实现方法移动应用在如今的数字化时代发挥着越来越重要的作用。
然而,随着移动应用的普及,用户的安全和隐私问题也日益凸显。
在这种情况下,实现安全登录和用户认证成为了移动应用开发的重要课题。
本文将介绍几种常见的移动应用中安全登录和用户认证的实现方法。
一、密码登录方式密码登录是目前最常见的用户认证方式之一。
通过让用户设置一个账号和密码,用户在登录时需要输入正确的账号和密码才能成功访问应用。
在实现密码登录时,开发者可以采取以下步骤来确保安全性:1. 强制密码复杂度:要求用户设置强密码,包括大小写字母、数字和特殊字符,并限制密码的最小长度。
2. 锁定账号:在连续多次输入错误密码后,将用户账号锁定一段时间,以防止暴力破解。
3. 加密存储密码:将用户密码进行哈希加密后再存储,确保即使数据库泄漏,黑客也无法获取用户的明文密码。
二、短信验证码方式随着短信技术的普及,短信验证码成为了一种常见的用户认证方式。
开发者可以通过以下步骤来实现:1. 要求用户绑定手机号码:在用户注册或登录时,要求用户绑定手机号码,以便发送验证码。
2. 发送验证码:向用户绑定的手机号码发送一条包含验证码的短信。
3. 验证验证码:让用户输入收到的验证码,并进行验证。
验证码应设置有效期,通常为一定分钟数。
三、指纹识别和面容识别方式随着手机设备的升级,越来越多的设备支持指纹识别和面容识别功能,这为移动应用的安全登录和用户认证提供了便利。
通过集成设备的指纹或面容识别功能,开发者可以实现以下步骤:1. 开启指纹或面容识别权限:应用需要向用户请求开启指纹或面容识别功能的权限。
2. 采集指纹或面容信息:将用户的指纹或面容信息采集并保存在设备中。
3. 验证指纹或面容信息:用户在登录时通过指纹或面容识别验证身份。
四、OAuth方式OAuth是一种开放标准协议,用于授权第三方应用访问用户的数据。
通过OAuth,用户可以使用他们已有的账号(比如Google、Facebook等)直接登录第三方应用。
单点登录的几种实现代码
单点登录的几种实现方式什么是单点登录?单点登录(Single Sign-On,简称SSO)是一种身份验证机制,允许用户只需登录一次,即可访问多个相互信任的应用程序或系统。
在传统的身份验证方式中,用户需要为每个应用程序输入用户名和密码,而SSO则通过一次登录即可实现用户在多个应用程序中的身份验证。
单点登录的实现可以简化用户的登录流程,提高用户体验,同时也方便了企业管理用户权限和安全性。
下面将介绍几种常见的单点登录实现方式。
1. 基于Cookie的单点登录基于Cookie的单点登录是最常见的实现方式之一。
该方式的原理是在用户登录成功后,将用户的身份信息保存在Cookie中,并在用户访问其他应用程序时,将Cookie中的身份信息发送给应用程序进行验证。
实现步骤如下:1.用户访问应用程序A,并尚未登录。
2.用户被重定向到登录页面,输入用户名和密码。
3.应用程序A验证用户的身份,登录成功后生成一个唯一的标识符(如Session ID),将该标识符保存在Cookie中,并发送给用户的浏览器。
4.用户访问应用程序B。
5.应用程序B检查用户的请求中是否包含Cookie,并解析出其中的标识符。
6.应用程序B使用标识符向应用程序A发送请求,验证标识符的有效性。
7.应用程序A返回验证结果给应用程序B。
8.应用程序B根据验证结果决定是否允许用户访问。
基于Cookie的单点登录实现简单,但也存在一些问题。
例如,Cookie可能被篡改或盗取,导致安全性问题。
同时,由于Cookie的大小有限制,如果用户访问的应用程序过多,可能会导致Cookie过大,影响性能。
2. 基于Token的单点登录基于Token的单点登录是一种无状态的实现方式,不需要在服务器端保存用户的登录状态。
该方式的原理是用户在登录成功后,服务器生成一个令牌(Token)并返回给用户,用户在后续的请求中携带该令牌进行身份验证。
实现步骤如下:1.用户访问应用程序A,并尚未登录。
单点登录解决方案
单点登录解决方案在互联网时代,用户需要记住多个账号和密码来登录不同的应用和网站,不仅容易忘记、繁琐,还存在着安全隐患。
为了解决这个问题,单点登录(Single Sign-On,简称SSO)应运而生。
单点登录是一种被广泛应用的身份验证机制,它允许用户只需一次登录,就可以访问多个相关应用和网站,极大地提高了用户体验和便利性。
本文将介绍单点登录的定义、工作原理以及常见的解决方案。
一、单点登录的定义单点登录是一种身份验证方法,它使用户只需一次登录就能够访问多个相关的应用和网站。
简单来说,用户只需输入一次账号和密码,就能够自由地切换不同的应用和网站,无需再次输入登录凭证。
这种方式不仅提高了用户体验,还能减轻用户记忆和管理多个账号密码的负担。
二、单点登录的工作原理单点登录的工作原理基于标识提供者(Identity Provider,简称IdP)和服务提供者(Service Provider,简称SP)之间的协作。
具体流程如下:1. 用户访问某个服务提供者的应用或网站。
2. 服务提供者将用户重定向到标识提供者的登录页面。
3. 用户在标识提供者的登录页面输入账号和密码进行身份验证。
4. 标识提供者验证用户的身份并生成一个令牌(Token)。
5. 标识提供者将令牌返回给服务提供者。
6. 服务提供者使用令牌进行身份验证,并向用户提供相应的服务。
通过以上流程,用户只需进行一次登录,即可访问多个相关的应用和网站,实现了单点登录的目的。
三、常见的单点登录解决方案单点登录的实现有多种解决方案,下面我们将介绍一些常见的解决方案:1. 基于CAS的单点登录CAS(Central Authentication Service)是一种开源的单点登录协议和框架。
它通过一个中央认证服务器来完成认证工作,其他应用和网站作为客户端与认证服务器进行通信。
CAS的优点是简单、可扩展性强,适用于多种环境和语言。
2. 基于OAuth的单点登录OAuth(Open Authorization)是一种用于授权的开放标准,它允许用户授权第三方应用访问他们在某个服务提供者上存储的信息。
auth2.0 原理流程及其单点登录和权限控制
auth2.0 原理流程及其单点登录和权限控制1. 什么是auth2.0?随着互联网的快速发展和应用程序的复杂性增加,用户身份验证和授权成为了一个非常重要的需求。
而OAuth 2.0(以下简称auth2.0)作为目前较为流行的身份验证和授权框架,被广泛地应用于互联网应用程序之中。
在本篇文章中,我们将深入探讨auth2.0的原理流程及其在单点登录和权限控制方面的应用。
2. auth2.0的原理流程在了解auth2.0的单点登录和权限控制之前,我们首先需要了解auth2.0的原理流程。
auth2.0作为一个授权框架,主要用于授权第三方应用程序访问用户在某一网站上存储的受保护资源,而不需要将用户的凭据暴露给第三方应用程序。
auth2.0的流程大致可以分为四个步骤:认证、授权、获取访问令牌和访问受保护资源。
在认证过程中,用户向客户端提供身份验证,并获得授权许可。
在授权过程中,客户端将用户引导至授权服务器,并获得授权许可。
在获取访问令牌的过程中,客户端通过授权许可向授权服务器请求令牌。
在访问受保护资源的过程中,客户端可以使用令牌来访问受保护资源。
3. auth2.0在单点登录中的应用单点登录(SSO)是一种身份验证服务,允许用户使用一组凭据来访问多个应用程序。
auth2.0可以作为实现单点登录的一种方式,通过它的授权码模式和令牌模式来实现单点登录的功能。
在使用授权码模式进行单点登录时,用户只需要登录一次,然后便可以在不同的应用程序中使用获取的访问令牌来进行访问,从而实现了单点登录的效果。
而在使用令牌模式进行单点登录时,用户通过令牌的方式来进行认证和授权,实现了跨应用程序的身份验证。
4. auth2.0在权限控制中的应用除了在单点登录中的应用外,auth2.0还可以被用于实现权限控制。
通过使用auth2.0的授权许可和访问令牌,应用程序可以对用户的访问进行控制,并实现不同级别的权限管理。
在权限控制中,auth2.0可以将用户分为不同的角色和组,然后根据用户的角色和组来进行访问控制。
php 扫码登录原理
php 扫码登录原理
PHP扫码登录原理是基于OAuth 2.0协议实现的。
OAuth 2.0是
一种授权框架,用于在不直接提供用户名和密码的情况下,允许用
户授权第三方应用访问其受保护的资源。
下面是PHP扫码登录的一
般原理:
1. 用户打开第三方应用的登录页面,选择使用扫码登录。
2. 第三方应用生成一个唯一的状态码,并将其存储在服务器端,同时生成一个二维码,其中包含该状态码作为参数。
3. 第三方应用将生成的二维码展示给用户。
4. 用户使用手机或其他设备的扫码工具扫描二维码。
5. 扫描工具将二维码中的参数(即状态码)发送给第三方应用
的服务器。
6. 第三方应用的服务器接收到状态码后,与之前存储的状态码
进行比对,验证其有效性。
7. 如果状态码有效,第三方应用服务器生成一个授权码,并将
其存储在服务器端,同时将授权码返回给扫描工具。
8. 扫描工具将授权码发送给第三方应用的服务器。
9. 第三方应用服务器接收到授权码后,验证其有效性,并使用
该授权码向认证服务器请求访问令牌。
10. 认证服务器验证授权码并颁发访问令牌给第三方应用服务器。
11. 第三方应用服务器使用访问令牌获取用户信息,如用户ID、用户名等。
12. 第三方应用服务器将获取到的用户信息与本地账户进行关联,完成扫码登录过程。
需要注意的是,以上是一种常见的PHP扫码登录原理,具体实
现可能会因应用需求而有所不同。
此外,为了保证安全性,还需要
在整个流程中加入必要的加密和验证机制,以防止恶意攻击和信息
泄漏。
移动应用开发中的社交登录与分享技术介绍
移动应用开发中的社交登录与分享技术介绍移动应用的兴起已经改变了我们的生活方式,而社交登录与分享技术在移动应用发展中扮演着重要的角色。
本文将介绍社交登录与分享技术的基本原理和实现方式,并展望其未来的发展趋势。
一、社交登录技术的原理与实现方式社交登录技术允许用户使用他们在社交媒体平台上的账号信息进行登录,避免了传统的用户名和密码方式。
它的优势在于方便快捷,减少注册流程,提高用户体验。
社交登录的原理是通过OAuth协议实现的。
OAuth协议允许第三方应用在用户授权的情况下,访问用户在社交媒体平台上的个人信息。
用户只需要点击一下授权按钮,就可以将自己的信息传输给第三方应用,实现一键登录。
社交登录的实现方式有多种,最常见的是通过SDK或API接入社交媒体平台的登录系统。
SDK会提供登录按钮以及相关授权接口,方便开发者进行集成。
API 则可以通过请求服务器所提供的接口,来实现社交登录功能。
二、社交分享技术的原理与实现方式社交分享技术可以让用户将应用中的内容分享给社交媒体上的好友,实现信息传播和推广的效果。
例如,用户在游戏中获得高分时,可以将成绩分享到社交媒体上,与好友们一起炫耀成就。
社交分享的原理是通过API实现的。
开发者可以调用社交媒体平台所提供的API接口,将应用中的内容传输到社交媒体上。
这些API接口通常包括认证授权、分享内容、获取好友列表等功能。
社交分享的实现方式也有多种,最常用的是SDK集成。
开发者可以通过SDK 调用API接口,实现分享功能。
同时,社交媒体平台还提供了分享按钮,可以在应用中添加分享按钮,用户点击后即可进行分享。
三、社交登录与分享技术的未来发展随着移动应用的不断发展,社交登录与分享技术也在不断创新和改进。
未来,我们可以期待以下几个方面的发展:1. 强化用户隐私保护:随着用户对个人隐私的关注增加,社交登录与分享技术需要更好地保护用户的隐私。
平台应该加强对用户数据的保护,提高用户的数据控制权。
nodejs实现OAuth2.0授权服务认证
nodejs实现OAuth2.0授权服务认证OAuth是⼀种开发授权的⽹络标准,全拼为open authorization,即开放式授权,最新的协议版本是2.0。
举个栗⼦:有⼀个"云冲印"的⽹站,可以将⽤户储存在Google的照⽚,冲印出来。
⽤户为了使⽤该服务,必须让"云冲印"读取⾃⼰储存在Google上的照⽚。
传统⽅法是,⽤户将⾃⼰的Google⽤户名和密码,告诉"云冲印",后者就可以读取⽤户的照⽚了。
这样的做法有以下⼏个严重的缺点。
1. "云冲印"为了后续的服务,会保存⽤户的密码,这样很不安全。
2. Google不得不部署密码登录,⽽我们知道,单纯的密码登录并不安全。
3. "云冲印"拥有了获取⽤户储存在Google所有资料的权⼒,⽤户没法限制"云冲印"获得授权的范围和有效期。
4. ⽤户只有修改密码,才能收回赋予"云冲印"的权⼒。
但是这样做,会使得其他所有获得⽤户授权的第三⽅应⽤程序全部失效。
5. 只要有⼀个第三⽅应⽤程序被破解,就会导致⽤户密码泄漏,以及所有被密码保护的数据泄漏。
所以OAuth就诞⽣了!1. Third-party application:第三⽅应⽤程序,本⽂中⼜称"客户端"(client),即上⼀节例⼦中的"云冲印"。
2. HTTP service:HTTP服务提供商,本⽂中简称"服务提供商",即上⼀节例⼦中的Google。
3. Resource Owner:资源所有者,本⽂中⼜称"⽤户"(user)。
4. User Agent:⽤户代理,本⽂中就是指浏览器。
5. Authorization server:认证服务器,即服务提供商专门⽤来处理认证的服务器。
6. Resource server:资源服务器,即服务提供商存放⽤户⽣成的资源的服务器。
[C#]自己开发实现OAuth做webapi认证教程大全
[C#]⾃⼰开发实现OAuth做webapi认证教程⼤全⾃⼰开发实现OAuth做webapi认证看到园⼦⾥⾯有⼈写的OAuth,就想把⾃⼰实现的OAuth也分享⼀下,关于OAuth协议这⾥就不再赘述。
⼀、作为认证服务器,⾸先需要提供⼀个可以通过appid/appsecret来获取token这样的⼀个接⼝,于是便有了以下代码。
public class AuthController : ApiController{[HttpGet]public HttpResponseMessage Token(string appid = "", string appsecret = ""){ApiResponseEntity rep;var isv = AppManage.Instance.GetAppISV(appid, appsecret);if (isv != null){string token = TokenManage.Instance.CreateToken(appid);rep = new ApiResponseEntity{Status = InterfaceStatus.Success,BizData = new{AccessToken = token}};}else{rep = new ApiResponseEntity(){Status = InterfaceStatus.Parm_Missing,Message = "param error"};}return rep.ToHttpResponseMessage();}}View Code创建token的算法可以⾃⾏实现,我是将新⽣成的Guid做了⼀下md5处理,代码如下:public string CreateToken(string appid){string token = Guid.NewGuid().ToString().ToMd5();Set(token, appid);return token;}View Code上⽂可以看到,在⽣成token了以后,就⼀个SetToken,就是将token存储在缓存⾥⾯,并设置了⼀定时间的⽣存周期,代码如下:public void Set(string token, string appid){var config = ServerConfigManage.Instance.GetServerConfig();string key = string.Format(RedisCacheKey.App_Token, token);RedisNetHelper.Set<string>(key, appid, DateTime.Now.AddSeconds(config.TokenSurvivalTime));}View Code为什么要⽤token做key,是因为token的变更会导致isv token验证失效,但是⽤token做key就可以在存活周期内,这个key都可以使⽤,避免了多线程获取token,或是其他原因导致的token失效。
使用SpringSecurityOAuth2配置自定义Token实现OAuth2授权示例
使⽤SpringSecurityOAuth2配置⾃定义Token实现OAuth2授权⽰例 本⽂记录⼀下使⽤SpringSecurityOAuth2配置⾃定义Token实现OAuth2授权的步骤 1、相关知识 OAuth协议简介: OAuth 2.0官⽹: 使⽤SpringSecurityOAuth2默认实现OAuth2授权⽰例: 2、构建项⽬ 本⽂使⽤的springboot版本是2.0.4.RELEASE,不同版本可能会有所区别。
下⾯是主要的配置⽂件和类: 1)pom依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.security.oauth.boot</groupId><artifactId>spring-security-oauth2-autoconfigure</artifactId><version>2.1.3.RELEASE</version></dependency> 2)application.properties#不需要,暂时写死在代码中,重构时移植到此处即可 3)主配置类@EnableWebSecurity@Configurationpublic class WebSecurityConfig extends WebSecurityConfigurerAdapter{@Overrideprotected void configure(HttpSecurity http) throws Exception {http.httpBasic().and().csrf().disable();}@Bean("authenticationManager")public AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}} 4)⽤户认证类@Componentpublic class MyUserDetailsService implements UserDetailsService{@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {System.out.println("登录⽤户名:"+username);String password = passwordEncoder.encode("123456");return new User(username,password,true,true,true,true,maSeparatedStringToAuthorityList("admin,ROLE_USER"));}} 5)认证服务类@Configuration@EnableAuthorizationServerpublic class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter{@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsService);}@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory()//Token保存在内存中.withClient("MyProject").secret(passwordEncoder.encode("MyProject_123"))//指明client-id和client-secret.accessTokenValiditySeconds(7200)//令牌有效时间,单位秒.authorizedGrantTypes("refresh_token","password","authorization_code")//⽀持刷新令牌、密码模式、授权码模式.scopes("all","read","write")//权限有哪些,如果这两配置了该参数,客户端发请求可以不带参数,使⽤配置的参数.redirectUris("http://127.0.0.1:8080/login");}} 说明: a)client-secret必须加密,否则在后⾯测试中,总是弹出让输⼊⽤户名、密码。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
数据库准备CREATE TABLE `oauth_client` (`id` bigint(20) NOT NULL auto_increment,`client_id` varchar(32) NOT NULL,`client_secret` varchar(32) NOT NULL,`redirect_uri` varchar(200) NOT NULL,`create_time` int(20) default NULL,PRIMARY KEY (`id`)) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;CREATE TABLE `oauth_code` (`id` bigint(20) NOT NULL auto_increment,`client_id` varchar(32) NOT NULL,`user_id` varchar(32) NOT NULL,`code` varchar(40) NOT NULL,`redirect_uri` varchar(200) NOT NULL,`expires` int(11) NOT NULL,`scope` varchar(250) default NULL,PRIMARY KEY (`id`)) ENGINE=MyISAM DEFAULT CHARSET=utf8;CREATE TABLE `oauth_token` (`id` bigint(20) NOT NULL auto_increment,`client_id` varchar(32) NOT NULL,`user_id` varchar(32) NOT NULL,`access_token` varchar(40) NOT NULL,`refresh_token` varchar(40) NOT NULL,`expires` int(11) NOT NULL,`scope` varchar(200) default NULL,PRIMARY KEY (`id`)) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;配置文件配置文件config.php'OAUTH2_CODES_TABLE'=>'oauth_code','OAUTH2_CLIENTS_TABLE'=>'oauth_client','OAUTH2_TOKEN_TABLE'=>'oauth_token',如果OAuth的服务器不是当前服务器,那就要指定下DSN地址了:'OAUTH2_DB_DSN'=>'mysql://root:mima@l:3306/database'你可以在/p/oauth2-php/ 找到源代码,上面实现了PDO和MongoDB 的数据模式。
下载下来的包解压,把Lib下的OAuth.inc改名为OAuth2.class.php后放到thinkphp核心包下的目录下:/Extend/Library/ORG/OAuth/OAuth2.class.php接下来我们要继承这个类;在这个目录下新建一个ThinkOAuth2.class.php文件:<?php// OAUTH2_DB_DSN 数据库连接DSN// OAUTH2_CODES_TABLE 服务器表名称// OAUTH2_CLIENTS_TABLE 客户端表名称// OAUTH2_TOKEN_TABLE 验证码表名称import("ORG.OAuth.OAuth2");class ThinkOAuth2 extends OAuth2 {private $db;private $table;/*** 构造函数:数据表配置*/public function __construct() {parent::__construct();$this ->db = Db::getInstance(C('OAUTH2_DB_DSN'));$this -> table = array('auth_codes'=>C('OAUTH2_CODES_TABLE'),'clients'=>C('OAUTH2_CLIENTS_TABLE'),'tokens'=>C('OAUTH2_TOKEN_TABLE'));}/*** 析构函数:关闭连接function __destruct() {$this->db = NULL; // Release db connection}private function handleException($e) {echo "Database error: " . $e->getMessage();exit;}/*** 增加客户端client* @param string $client_id* @param string $client_secret* @param string $redirect_uri*/public function addClient($client_id, $client_secret, $redirect_uri) {$time = time();$sql = "INSERT INTO {$this ->table['clients']} "."(client_id, client_secret, redirect_uri, create_time) VALUES (\"{$client_id}\", \"{$client_secret}\", \"{$redirect_uri}\",\"{$time}\")";$this ->db ->execute($sql);}/*** 实现OAuth2::checkClientCredentials()* @参考OAuth2::checkClientCredentials()*/protected function checkClientCredentials($client_id, $client_secret = NULL) {$sql = "SELECT client_secret FROM {$this ->table['clients']} "."WHERE client_id = \"{$client_id}\"";$result = $this ->db ->query($sql);if ($client_secret === NULL) {return $result !== FALSE;}//Log::write("checkClientCredentials : ".$result);//Log::write("checkClientCredentials : ".$result[0]);//Log::write("checkClientCredentials : ".$result[0]["client_secret"]);return $result[0]["client_secret"] == $client_secret;}* 实现OAuth2::getRedirectUri().* @see OAuth2::getRedirectUri()*/protected function getRedirectUri($client_id) {$sql = "SELECT redirect_uri FROM {$this ->table['clients']} "."WHERE client_id = \"{$client_id}\"";$result = $this ->db ->query($sql);if ($result === FALSE) {return FALSE;}//Log::write("getRedirectUri : ".$result);//Log::write("getRedirectUri : ".$result[0]);//Log::write("getRedirectUri : ".$result[0]["redirect_uri"]);returnisset($result[0]["redirect_uri"]) && $result[0]["redirect_uri"] ? $result[0]["redirect_uri"] : NULL;}/*** 实现OAuth2::getAccessToken().* @see OAuth2::getAccessToken()*/protected function getAccessToken($access_token) {$sql = "SELECT client_id, expires, scope FROM {$this ->table['tokens']} "."WHERE access_token = \"{$access_token}\"";$result = $this ->db ->query($sql);//Log::write("getAccessToken : ".$result);//Log::write("getAccessToken : ".$result[0]);return $result !== FALSE ? $result : NULL;}/*** Implements OAuth2::setAccessToken().* @see OAuth2::setAccessToken()*/protected function setAccessToken($access_token, $client_id, $expires, $scope = NULL) {$sql = "INSERT INTO {$this ->table['tokens']} "."(access_token, client_id, expires, scope) "."VALUES (\"{$access_token}\", \"{$client_id}\", \"{$expires}\", \"{$scope}\")";$this ->db ->execute($sql);}/*** Overrides OAuth2::getSupportedGrantTypes().* @see OAuth2::getSupportedGrantTypes()*/protected function getSupportedGrantTypes() {return array(OAUTH2_GRANT_TYPE_AUTH_CODE);}/*** Overrides OAuth2::getAuthCode().* @see OAuth2::getAuthCode()*/protected function getAuthCode($code) {$sql = "SELECT code, client_id, redirect_uri, expires, scope "."FROM {$this ->table['auth_codes']} WHERE code = \"{$code}\"";$result = $this ->db ->query($sql);//Log::write("getAuthcode : ".$result);//Log::write("getAuthcode : ".$result[0]);//Log::write("getAuthcode : ".$result[0]["code"]);return $result !== FALSE ? $result[0] : NULL;}/*** Overrides OAuth2::setAuthCode().* @see OAuth2::setAuthCode()*/protected function setAuthCode($code, $client_id, $redirect_uri, $expires, $scope = NULL) {$time = time();$sql = "INSERT INTO {$this ->table['auth_codes']} "."(code, client_id, redirect_uri, expires, scope) "."VALUES (\"${code}\", \"${client_id}\", \"${redirect_uri}\", \"${expires}\", \"${scope}\")";$result = $this ->db ->execute($sql);}/*** Overrides OAuth2::checkUserCredentials().* @see OAuth2::checkUserCredentials()*/protected function checkUserCredentials($client_id, $username, $password){return TRUE;}}认证方案及实现OAuth2 RFC 6749 规范提供了四种基本认证方案,以下针对这四种认证方案以及它们在本实现中的使用方式进行分别说面。