目錄
? ? 1 Socket
? ? 2 軟件結(jié)構(gòu)
? ? 3 Servlet
? ? 4 HTTP
????5 單點(diǎn)登錄SSO
? ? 6 常見問題總結(jié)參考資料
? ? · 《深入分析 Java Web》
1 Socket
1.1 概念
? ? 套接字(socket)肛真,是描述計(jì)算機(jī)之間完成相互網(wǎng)絡(luò)通信的抽象功能,沒有對應(yīng)的實(shí)體蝶糯。通訊的兩端都有Sokcet,數(shù)據(jù)在兩個Sokcet間通過IO傳輸渣叛。
? ? 格式為:IP:端口號
1.2 分類
? ? · TCP和UDP
? ? ·?兩種方式傳輸數(shù)據(jù)都是通過序列化java對象后粘秆,通過二進(jìn)制協(xié)議傳輸粹胯,故Socket通信和編程語言沒有關(guān)系。
2 軟件結(jié)構(gòu)
2.1 C/S體系
? ? Client-Server 客戶端-服務(wù)器端咨演。屬于桌面應(yīng)用程序闸昨。
? ? · 弊端:
? ? ? ? ·需要安裝軟件
? ? ? ? ·維護(hù)難,占空間
? ? ? ? ·服務(wù)端升級,客戶端也需要升級
? ? · 優(yōu)點(diǎn):
? ? ? ? ·體驗(yàn)效果好
? ? ? ? ·占寬帶小
? ? ? ? ·減輕服務(wù)器端壓力
2.2 B/S體系
? ? Browser-Server瀏覽器端-服務(wù)器端饵较,屬于網(wǎng)站應(yīng)用程序拍嵌。
? ? · 弊端:
? ? ? ? ·需要使用瀏覽器訪問
? ? ? ? ·兼容性差、安全性差
? ? ? ? ·服務(wù)器端壓力大
? ? · 優(yōu)點(diǎn):
? ? ? ? ·不需要特定客戶端
? ? ? ? ·服務(wù)端升級循诉,瀏覽器不需要升級横辆。
3 Servlet
3.1 資源的分類
? ? · 靜態(tài)資源:當(dāng)用戶多次訪問這個資源,資源的源代碼永遠(yuǎn)不會改變的資源茄猫。
? ? · 動態(tài)資源:當(dāng)用戶多次訪問這個資源狈蚤,資源的源代碼可能會發(fā)送改變。
3.2 實(shí)質(zhì)
? ? Servlet本質(zhì)是Server Applet 服務(wù)連接器划纽。是服務(wù)器端的程序脆侮,具有獨(dú)立平臺和協(xié)議的特性,用于交互式地瀏覽和生成數(shù)據(jù)勇劣,生成動態(tài)Web內(nèi)容靖避。
3.3 使用
? ? 編寫類繼承HttpServlet類,并覆蓋doGet和doPost方法比默,并在web.xml文件或者使用@WebServlet注解配置訪問路徑幻捏。
public?class?HelloServlet?extends?HttpServlet{
????@Override
? ??protected?void?doGet(HttpServletRequest req, HttpServletResponse resp)?throws?ServletException, IOException {
????????//解決中文亂碼問題
???????resp.setCharacterEncoding("utf-8");//內(nèi)容編碼,防止出現(xiàn)中文亂碼
????????resp.setContentType("text/html;charset=utf-8"); //向?yàn)g覽器輸出內(nèi)容
????????resp.getWriter().write("這是第一個servlet程序。當(dāng)前時間為:"+new?Date());
????}
}
3.4 工作原理
3.4.1 Servlet容器
? ? servlet容器的產(chǎn)生是為了技術(shù)上的解耦,通過標(biāo)準(zhǔn)化接口來相互協(xié)作。? ??
? ? 圖中看出,真正管理servlet的容器是Context容器鳍寂,且一個Context對應(yīng)一個Web工程。
3.4.2 Servlet容器的啟動
Tomcat啟動代碼
Tomcat tomcat=getTomcatInstance();
File appDir= new File(getBuildDirectory(), "webapps/examples") ;
tomcat. addWebapp(null, "/examplesappDir. getAbsolutePath());
tomcat. start();
ByteChunk res = geturl( "http://localhost:"+getport()+" /examples/servlets/servlet/HelloWorldExample");
assertTrue(res tostring().indexOf(<h1>Hello World! </h1>")>0);
public Context addwebapp (Hosthost, String url, String path){
????silence(url);
????Context ctx? = new StandardContext();
? ? ?ctx.setPath( url);
?????ctx. setDocBase(path);
?????if(defaultRealm== null){
?????????initsimpleAuth();
? ? }
?????ctx. setRealm(defaultRealm);
? ? ?ctx. addLifecycleListener(new DefaultWebXmlListener());
?????Contextconfig ctxCfg = new ContextConfig();
?????ctx. addLifecycleListener(ctxCfg);
? ? ?ctxcfg. setDefaultwebXml ("org/apache/catalin/startup/DEFAULT_XMI ") ;
? ? ?if(host == null){
? ? ? ? ?getHost(). addchild (ctx)勺鸦;
? ? ?} else{
?????????host.addchild(ctx);
? ? ?}
? ? ?return ctx妨退;
(1)Tomcat啟動過程
? ? ????·getTomcatInstance()獲取Tomcat實(shí)例
? ? ? ? ·新建StandardContext()容器,并設(shè)置訪問URl和項(xiàng)目文件訪問地址
? ? ? ? ·添加LifecycleListener
? ? ? ? ·新建ContextConfig锭魔。解析Web應(yīng)用的配置文件web.xml等
? ? ? ? ·tomcat實(shí)例執(zhí)行addWebapp()
? ? ? ? ·tomcat實(shí)例執(zhí)行start()例证,啟動整個Tomcat容器。
(2)tomcat所有的容器都集成Lifecycle接口迷捧,Lifecycle接口管理著容器的整個生命周期织咧,所有容器的修改和狀態(tài)的改變都會由它去通知已經(jīng)注冊的觀察者(Listener)。這是基于觀察者模式設(shè)計(jì)的漠秋。
(3)Context容器的啟動
? ? ? ? 在tomcat容器啟動后笙蒙,當(dāng)Context容器處于init初始化狀態(tài)時,其中的Listener將會被調(diào)用庆锦。
????????首先執(zhí)行ContextConfig的Init方法:
? ? ? ? ? ? · 創(chuàng)建用于解析XML配置文件的contextDigester對象
? ? ? ? ? ? ·?讀取默認(rèn)的 context. xm配置文件,如果存在則解析它捅位。
? ? ? ? ? ? · 讀取默認(rèn)的Hos配置文件,如果存在則解析它
? ? ? ? ? ? · 讀取默認(rèn)的 Context自身的配置文件,如果存在則解析它
? ? ? ? ? ? · 設(shè)置 Context的 DocBase
????????ContextConfig的init方法完成后, Context容器就會執(zhí)行startInternal()方法,這個方法的啟動邏輯比較復(fù)雜,主要包括如下幾部分:
? ? ? ? ? ? · 創(chuàng)建讀取資源文件的對象
? ? ? ? ? ? · 創(chuàng)建 ClassLoader對象
? ? ? ? ? ? · 設(shè)置應(yīng)用的工作目錄
? ? ? ? ? ? · 啟動相關(guān)的輔助類,如 logger、 realm、 resources等艇搀。
? ? ? ? ? ? · 修改啟動狀態(tài),通知感興趣的觀察者(Web應(yīng)用的配置)
? ? ? ? ? ? · 子容器的初始化
? ? ? ? ? ? · 獲取 ServletContext并設(shè)置必要的參數(shù)尿扯。
? ? ? ? ? ? · 初始化“ load on startup”的 Servlet. 其他Servlet在第一次被調(diào)用的時候初始化。
? ? ? ? 在初始化時焰雕,會將Servlet包裝成StandardWrapper衷笋。因?yàn)镾tandardWrapper是Tomcat容器中的一部分,它具有容器的特性矩屁,而Servlet作為一個獨(dú)立的Web開發(fā)標(biāo)準(zhǔn)辟宗,不應(yīng)該強(qiáng)耦合在Tomcat中。
3.4.3 Servlet實(shí)例的建立和初始化
(1)創(chuàng)建實(shí)例
? ? ? ? web.xml中的配置項(xiàng)吝秕,“l(fā)oad-on-startup”如果大于0慢蜓,則在Tomcat啟動時Servlet就會被啟動。調(diào)用Wrapper.loadServlet方法獲取servletClass郭膛,并交給InstanceManager去創(chuàng)建對象晨抡。
(2)初始化實(shí)例
? ? ? ? 調(diào)用StandardWrapper的InitServlet方法初始化對象。
3.4.4 Servlet體系結(jié)構(gòu)
? ? · ServletConfig则剃,在Servlet初始化時就傳到Servlet耘柱,以StandardWrapperFacade對象的形式調(diào)用,可以防止暴露不必要的數(shù)據(jù)棍现。
? ? · ServletContext调煎,獲取Context容器的信息。
? ? · ServletRquest
? ? · ServletResponse
? ? 體系設(shè)計(jì)過程中用到門面設(shè)計(jì)模式己肮。
3.4.5 Servlet調(diào)用
(1)訪問URL:http://hostname:port/contextpath/servletPath
(2)Tomcat中的org.apache.tomcat.util.http.mapper完成URL到一個Servlet子容器的映射工作士袄。Mapper類保存了Tomcat的Container容器中的所有子容器的信息,根據(jù)傳入Servlet容器的請求的hostname和contextpath設(shè)置到request的mappingData屬性中谎僻。
(3)Mapper子類MapperListener作為監(jiān)聽這監(jiān)聽容器的變化娄柳,這樣其中的mapper屬性也相應(yīng)修改。
(4)執(zhí)行Servlet接口的service(ServletRequest req, ServletResponse resp)方法艘绍。
3.4.6 Filter
3.4.6.1 簡介
????Filter也稱之為過濾器赤拒,它是 Servlet 技術(shù)中最實(shí)用的技術(shù),Web 開發(fā)人員通過 Filter 技術(shù)诱鞠,對 web 服務(wù)器管理的所有 web 資源:例如 Jsp, Servlet, 靜態(tài)圖片文件或靜態(tài) html 文件等進(jìn)行攔截挎挖,從而實(shí)現(xiàn)一些特殊的功能。例如實(shí)現(xiàn) URL 級別的權(quán)限訪問控制航夺、過濾敏感詞匯蕉朵、壓縮響應(yīng)信息等一些高級功能。
????它主要用于對用戶請求進(jìn)行預(yù)處理阳掐,也可以對HttpServletResponse進(jìn)行后處理始衅。使用 Filter 的完整流程:Filter 對用戶請求進(jìn)行預(yù)處理堪伍,接著將請求交給 Servlet 進(jìn)行處理并生成響應(yīng),最后 Filter 再對服務(wù)器響應(yīng)進(jìn)行后處理觅闽。
3.4.6.2 Demo
/**
*? 使用Filter 打印參數(shù)
* @author Administrator
*
*/
public class FilterDemo implements Filter {
? ? public? FilterDemo(){
? ? ???? System.out.println("FilterDemo 構(gòu)造函數(shù)被執(zhí)行...");
? ? }
/**
* 銷毀
*/
????public void destroy() {
? ? ????System.out.println("destroy");
????}
/*
????用戶在每個請求進(jìn)來時帝雇,訪問doFilter方法,在Servlet的service方法之前調(diào)用蛉拙。*/
????public void doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse,
????????????????FilterChain paramFilterChain) throws IOException, ServletException {
? ? ????System.out.println("doFilter");
????????HttpServletRequest request = (HttpServletRequest) paramServletRequest;
????????HttpServletResponse response = (HttpServletResponse) paramServletResponse;
????????// 請求地址
????????String requestURI = request.getRequestURI();
????????System.out.println("requestURI:"+requestURI);
????????// 參數(shù)
????????Map<String, String[]> parameterMap = request.getParameterMap();
????????for (String key : parameterMap.keySet()) {
????????String[] arr=parameterMap.get(key);
????????}
????}
/**
* 初始化
*/
????public void init(FilterConfig paramFilterConfig) throws ServletException {
????????System.out.println("init");
????}
}
<filter>
? <filter-name>FilterDemo</filter-name>
? <filter-class>com.qian.servlet.FilterDemo</filter-class>
</filter>
<filter-mapping>
????<filter-name>FilterDemo</filter-name>
????<url-pattern>/*</url-pattern>
</filter-mapping>
3.4.6.3 相關(guān)類
(1)FilterConfig
? ? ? ? 可以通過其獲取ServletContext對象尸闸。
(2)FilterChain
? ? ? ? 責(zé)任鏈設(shè)計(jì)模式。FilterChain是doFilter方法的傳入?yún)?shù)孕锄,保存了當(dāng)前整個請求鏈吮廉。通過調(diào)用FilterChain.doFilter方法,可以將請求繼續(xù)傳遞下去畸肆,但是如果要攔截請求宦芦,則不調(diào)用。
? ? ? ? 當(dāng)FilterChain上所有的Filter對象執(zhí)行完成后轴脐,才會執(zhí)行最終的Servlet调卑。
3.4.6.4 注意事項(xiàng)
(1)Filter常用于登錄、XSS攻擊大咱、權(quán)限方面恬涧。
(2)Filter是單例的,與Servlet類似碴巾。
3.5 Servlet生命周期
3.5.1 Servlet接口方法
public class TestServletService implements Servlet{
????@Override
????public void destroy() {
????????// TODO 自動生成的方法存根
????}
????@Override
????public ServletConfig getServletConfig() {
????????// TODO 自動生成的方法存根
????????return null;
????}
????@Override
????public String getServletInfo() {
????????// TODO 自動生成的方法存根
????????return null;
????}
????@Override
????public void init(ServletConfig arg0) throws ServletException {
????????// TODO 自動生成的方法存根
????}
????@Override
????public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {
????????// TODO 自動生成的方法存根
????}
}
其中溯捆,與生命周期相關(guān)的有四個方法:
(1)構(gòu)造方法:創(chuàng)建servlet對象的時候調(diào)用。默認(rèn)情況下厦瓢,第一次訪問(擁有l(wèi)oad-on-startup設(shè)置的除外)servlet的時候創(chuàng)建servlet對象只調(diào)用1次提揍。證明servlet對象在tomcat是單實(shí)例的。
(2)init方法: 創(chuàng)建完servlet對象的時候調(diào)用煮仇。只調(diào)用1次劳跃。
(3)service方法: 每次發(fā)出請求時調(diào)用。調(diào)用n次欺抗。
(4)destroy方法: 銷毀servlet對象的時候調(diào)用售碳。停止服務(wù)器或者重新部署web應(yīng)用時銷毀servlet對象。只調(diào)用1次绞呈。
3.5.2 Tomcat內(nèi)部代碼運(yùn)行
(1)通過配置文件的映射關(guān)系,找到ServletClass內(nèi)容间景。
(2)通過反射構(gòu)造Servlet對象
(3)創(chuàng)建ServletConfig對象佃声,反射調(diào)用init方法。
(4)創(chuàng)建request倘要、response對象圾亏,反射調(diào)用service方法十拣。
(5)銷毀servlet時,反射調(diào)用destroy方法志鹃。
3.5.3 時序圖
3.6 Servlet多線程問題
(1)servlet對象在tomcat服務(wù)器是單實(shí)例多線程的夭问,為每個用戶請求分配一個線程,可以通過線程池來管理曹铃。
(2)因?yàn)閟ervlet是多線程的缰趋,所以當(dāng)多個servlet的線程同時訪問了servlet的共享數(shù)據(jù),如成員變量陕见,可能會引發(fā)線程安全問題秘血。
????????解決辦法:
?????????????· 把使用到共享數(shù)據(jù)的代碼塊進(jìn)行同步(使用synchronized關(guān)鍵字進(jìn)行同步)
? ? ? ? ? ? ?· 建議在servlet類中盡量不要使用成員變量。如果確實(shí)要使用成員评甜,必須同步灰粮。而且盡量縮小同步代碼塊的范圍。(哪里使用到了成員變量忍坷,就同步哪里U持邸!)佩研,以避免因?yàn)橥蕉鴮?dǎo)致并發(fā)效率降低蓖乘。
3.7 域?qū)ο?/h2>
(1)作用: 用于保存數(shù)據(jù)、獲取數(shù)據(jù)韧骗,可以在不同的動態(tài)資源之間共享數(shù)據(jù)嘉抒。
(2)使用:
????????· 保存數(shù)據(jù) setAttribute(String,Object)
????????· 獲取數(shù)據(jù) Object getAttribute(String)
????????· 刪除數(shù)據(jù) removeAttribute(String)
(3)分類:
? ? ? ? · HttpServletRequest
? ? ? ? · HttpSession 會話對象
? ? ? ? · PageContext
? ? ? ? · ServletContext 作用范圍為整個Web應(yīng)用
(4)可以通過getContextPath()方法獲取上下文路徑
3.8 轉(zhuǎn)發(fā)與重定向
(1)轉(zhuǎn)發(fā):
? ? ? ? · 地址欄不會改變
? ? ? ? · 只能轉(zhuǎn)發(fā)到當(dāng)前web應(yīng)用
? ? ? ? · 可以把數(shù)據(jù)保存到request域
? ? ? ? · request.getRequestDispatcher(String).forward(request袍暴,response)
(2)重定向:
? ? ? ? · 地址欄改變
? ? ? ? · 可以跳轉(zhuǎn)到其他web應(yīng)用
? ? ? ? · 不能使用request數(shù)據(jù)(涉及到2次瀏覽器請求)
? ? ? ? · response.sendRedirect(String)
3.9 Cookie
? ? 會話數(shù)據(jù)保存在客戶端些侍。瀏覽器在每次訪問服務(wù)端時,都會帶著cookie信息政模。
關(guān)于Cookie的跨域問題:cookie 跨域問題_chou_out_man的博客-CSDN博客_cookie跨域
3.9.1 使用
(1)構(gòu)造Cookie對象
????????Cookie(java.lang.String name, java.lang.String value)
(2)設(shè)置cookie
????????void setPath(java.lang.String uri) ?:設(shè)置cookie的有效訪問路徑
????????void setMaxAge(int expiry):設(shè)置cookie的有效時間
? ? ? ? ? ? ? ? · 不設(shè)置岗宣,則隨瀏覽器關(guān)閉而消失
? ? ? ? ? ? ? ? · 整數(shù)(正負(fù)均可),cookie保存到瀏覽器淋样,緩存在硬盤中
? ? ? ? ? ? ? ? · 零耗式,不保存cookie
????????void setValue(java.lang.String newValue) :設(shè)置cookie的值
(3)發(fā)送cookie到瀏覽器端保存
????????void response.addCookie(Cookie cookie) ?:發(fā)送cookie
(4)服務(wù)器接收cookie
????????Cookie[] request.getCookies() :接收cookie
3.9.2 Demo
@WebServlet("/LastAccessTime")
public class LastAccessTime extends HttpServlet {
????@Override
????protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
????????resp.setContentType("text/html;charset=utf-8");// 防止瀏覽器顯示亂碼
????????String lastAccessTime = null;
????????Cookie[] cookies = req.getCookies();
????????for (Cookie cookie : cookies) {
????????????String name = cookie.getName();
????????????if (name.equals("lastAccessTime")) {
????????????????astAccessTime = cookie.getValue();
????????????????break;
????????????}
????????}
????????if (StringUtils.isEmpty(lastAccessTime)) {
????????????????resp.getWriter().print("您是首次訪問!");
????????} else {
????????????????resp.getWriter().print("你上次訪問時間:" + lastAccessTime);
????????}
????// 保存訪問時間
????// 創(chuàng)建cookie 將當(dāng)前時間作為cookie保存到瀏覽器
????String currenttime = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss").format(new Date());
????Cookie cookie = new Cookie("lastAccessTime", currenttime);
????cookie.setMaxAge(60 * 60 * 24);
????// 發(fā)送cookie
????resp.addCookie(cookie);
????}
}
3.9.3 工作原理
? ? 真正構(gòu)建Cookie是在org.apache.catalina.connector.Response類中,調(diào)用generateCookieString方法將Cookie對象構(gòu)造成一個String字符串趁猴,并將字符串命名為Set-Cookie添加到Header中刊咳。
3.9.4 利弊分析
????弊端
? ? ? ? · 只能存字符串類型,不能保存對象
? ? ? ? · 只能存非中文
? ? ? ? · 1個Cookie的容量不超過4KB儡司,最多300個
? ? ? ? · 安全性差
3.9.5 壓縮cookie
(1)采用文本壓縮方式娱挨。可采用gzip或者deflate算法
(2)壓縮后進(jìn)行轉(zhuǎn)碼捕犬,采用Base32或者Base64跷坝。因?yàn)镃ookie中不能包含控制字符酵镜,只能包含ASCII碼中34~126的可見字符。
3.10 Session
? ? 會話數(shù)據(jù)保存在服務(wù)器端(內(nèi)存中)柴钻。
3.10.1 使用
HttpSession類:用于保存會話數(shù)據(jù)
(1)創(chuàng)建或得到session對象
????????HttpSession getSession()?
????????HttpSession getSession(boolean create)?
(2)設(shè)置session對象
????????void setMaxInactiveInterval(int interval)? : 設(shè)置session的有效時間
????????void invalidate()? ? : 銷毀session對象
????????java.lang.String getId()? : 得到session編號
(3)保存會話數(shù)據(jù)到session對象
????????void setAttribute(java.lang.String name, java.lang.Object value)? : 保存數(shù)據(jù)
????????java.lang.Object getAttribute(java.lang.String name)? : 獲取數(shù)據(jù)
????????void removeAttribute(java.lang.String name) : 清除數(shù)據(jù)
3.10.2 demo
@WebServlet("/TestSession")
public class TestSession extends HttpServlet {
????private static final long serialVersionUID = 1L;
? ? /**
? ? * @see HttpServlet#HttpServlet()
? ? */
? ? public TestSession() {
? ? ? ? super();
? ? ? ? // TODO Auto-generated constructor stub
? ? }
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
????protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
????????// TODO Auto-generated method stub
????????response.setContentType("text/html;charset=utf-8");// 防止瀏覽器顯示亂碼
????????HttpSession session = request.getSession();
????????String lastTime = (String) session.getAttribute("lastTime");
????????if(lastTime == null) {
????????????response.getWriter().write("這是第一次訪問");
????????}else {
????????????response.getWriter().write("上次訪問時間:" + lastTime + "現(xiàn)在: " + new Date());
????????}
????????session.setMaxInactiveInterval(60 * 60 * 24);
????????session.setAttribute("lastTime", new Date()+ "");
????}
}
3.10.3 與Cookie關(guān)系
(1)服務(wù)器生成唯一對應(yīng)session對象的JSESSIONID淮韭,并作為cookie發(fā)送到瀏覽器端保存。
(2)如果瀏覽器禁用cookie可以將JSESSIONID寫到用戶請求的URL中贴届。
(3)如何避免瀏覽器的JSESSIONID的cookie隨著瀏覽器關(guān)閉而丟失的問題
/**
* 手動發(fā)送一個硬盤保存的cookie給瀏覽器
*/
Cookie c = new Cookie("JSESSIONID",session.getId());
c.setMaxAge(60*60);
response.addCookie(c);
3.10.4 工作原理
(1)request.getSession()方法靠粪,觸發(fā)創(chuàng)建session對象,并加入到org.apache.catalina.Manager的session容器中保存粱腻。
(2)Manager負(fù)責(zé)servlet容器中所有session的生命周期管理庇配。當(dāng)servlet容器重啟或者關(guān)閉時,Manager負(fù)責(zé)持久化(調(diào)用upload方法)沒有過期的session對象到“SESSIONS.ser”文件中绍些,也會定期檢測過期捞慌。
3.10.5 分布式session
(1)服務(wù)訂閱服務(wù)器,集中管理資源和配置柬批,統(tǒng)一通過它來推送配置啸澡。可以使用Zookeeper實(shí)現(xiàn)氮帐。
(2)分布式緩存嗅虏,存儲共享集群中每臺集群的session。
(3)存取方式上沐。
? ? ? ? · 自定義InnerHttpSession類重新實(shí)現(xiàn)HttpSession接口皮服。
? ? ? ? · 通過Filter攔截用戶請求,將自己設(shè)置的InnerHttpSession對象設(shè)置到request和response對象中参咙。
? ? ? ? · 應(yīng)用創(chuàng)建的所有Session對象都保存在InnerHttpSession對象中龄广,訪問完成后將InnerHttpSession內(nèi)容更新到分布式緩存中。
3.10.6 跨域名共享Cookie
(1)利用跳轉(zhuǎn)應(yīng)用支持多個域名的訪問蕴侧,并將同一個sessionID作為cookie寫到多個域名下择同。
(2)多個域名,根據(jù)sessionID在分布緩存中拿取session信息净宵。
3.10.7 表單重復(fù)提交問題
(1)原因
? ? ? ? · 網(wǎng)絡(luò)延時
? ? ? ? · 瀏覽器重新刷新按鈕
? ? ? ? · 瀏覽器“回退”按鈕敲才,再提交。
(2)解決方案
? ? ? ? · 利用javaScript代碼進(jìn)行標(biāo)識
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Form表單</title>
<script type="text/javascript">
????var isFlag = false; //表單是否已經(jīng)提交標(biāo)識择葡,默認(rèn)為false
????function submitFlag() {
????if (isFlag == false) {
????????isFlag = true;
????????return true;
????} else {
????????return false;
????}
}
</script>
</head>
<body>
<form action="${pageContext.request.contextPath}/DoFormServlet"? ?method="post" onsubmit="return submitFlag()">
????用戶名:<input type="text" name="userName"> <input type="submit"? ?value="提交" id="submit">
</form>
</body>
</html>
? ? ? ? ·利用js讓按鈕在提交一次后不可用
function dosubmit(){
? ? //獲取表單提交按鈕
? ? var btnSubmit = document.getElementById("submit");
? ? //將表單提交按鈕設(shè)置為不可用紧武,這樣就可以避免用戶再次點(diǎn)擊提交按鈕
? ? btnSubmit.disabled= "disabled";
? ? //返回true讓表單可以正常提交
? ? return true;
}
? ? ? ? · 在session域中生成并保存唯一token,在頁面中加入隱藏域存儲token刁岸,提交時進(jìn)行檢驗(yàn)
//用戶訪問服務(wù)器
@WebServlet("/ForwardServlet")
public class ForwardServlet extends HttpServlet {
@Override
????protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
????????req.getSession().setAttribute("sesionToken", TokenUtils.getToken());
????????req.getRequestDispatcher("form.jsp").forward(req, resp);
????}
}
//跳轉(zhuǎn)頁面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Form表單</title>
</head>
????<body>
????????<form action="${pageContext.request.contextPath}/DoFormServlet"? method="post" onsubmit="return dosubmit()">
????????????<input type="hidden" name="token" value="${sesionToken}"> 用戶名:<input type="text"
????????????????name="userName"> <input type="submit" value="提交" id="submit">
????????</form>
????</body>
</html>
//服務(wù)器后端處理
HttpSession session = request.getSession();
String sesionToken = (String) session.getAttribute("sesionToken");
String token = request.getParameter("token");
if (!(token.equals(sesionToken))) {
return false;
}
session.removeAttribute("sesionToken");
3.11 Token
? ? 隨機(jī)性令牌脏里,唯一不重復(fù)字符串。
? ? 生成方式:
? ? ? ? ? ? · 自定義唯一識別碼
? ? ? ? ? ? · UUID.randomUUID().toString()
3.12 Web安全與攻防
(1)XSS 跨站腳本注入
? ? ? ? 利用Filter進(jìn)行攔截虹曙,例如將<script>轉(zhuǎn)化為html元素 <script>迫横。
(2)CSRF 跨站請求偽造,避免Cookie酝碳,利用token矾踱。
(3)上傳漏洞、可執(zhí)行exe文件疏哗。
(4)Sql注入 利用#{}和?占位符解決呛讲,避免sql語句的拼接。
4 HTTP
4.1 簡介
? ? 超文本傳輸協(xié)議返奉,是瀏覽器個服務(wù)端之間數(shù)據(jù)傳輸?shù)母袷揭?guī)范贝搁。HTTP使用TCP作為傳輸層協(xié)議,但HTTP本身是無連接芽偏、無狀態(tài)的雷逆,可以短連接(Http/1.0),也可以長連接(Http/1.1 保持一段時間)污尉。
4.2 Http請求
4.2.1 請求格式
GET /day09/hello?HTTP/1.1? -請求行
Host: localhost:8080? ? --請求頭(多個key-value對象)
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,en-us;q=0.8,zh;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
--一個空行
name=eric&password=123456 ????????????--(可選)實(shí)體內(nèi)容
4.2.2 請求行
GET /day09/hello?HTTP/1.1
(1)請求方式
????常見的請求方式: GET 膀哲、 POST、 HEAD被碗、 TRACE某宪、 PUT、 CONNECT 锐朴、DELETE
????常用的請求方式: GET? 和 POST
? ? · GET
? ? ? ? ? ? · URL后面可以跟參數(shù)
? ? ? ? ? ? · 參數(shù)限制為1KB內(nèi)
? ? ? ? ? ? · 默認(rèn)方式兴喂,但不適合提交敏感密碼等內(nèi)容
? ? · POST
? ? ? ? ? ? · 參數(shù)在請求體中
? ? ? ? ? ? · 參數(shù)數(shù)據(jù)無限制
? ? ? ? ? ? · 適合提交敏感內(nèi)容
(2)http協(xié)議版本
????http1.0:當(dāng)前瀏覽器客戶端與服務(wù)器端建立連接之后,只能發(fā)送一次請求焚志,一次請求之后連接關(guān)閉衣迷。
????http1.1:當(dāng)前瀏覽器客戶端與服務(wù)器端建立連接之后,可以在一次連接中發(fā)送多次請求娩嚼。(基本都使用1.1)
(3)請求資源
????URL:? 統(tǒng)一資源定位符蘑险。http://localhost:8080/day09/testImg.html。只能定位互聯(lián)網(wǎng)資源岳悟。是URI 的子集佃迄。
????URI: 統(tǒng)一資源標(biāo)記符。/day09/hello贵少。用于標(biāo)記任何資源呵俏。可以是本地文件系統(tǒng)滔灶,局域網(wǎng)的資源(//192.168.14.10/myweb/index.html)普碎, 可以是互聯(lián)網(wǎng)。
4.2.3 請求頭
? ? · 多個key-value對象
? ? · 含有配置信息和cookie
4.2.4 實(shí)體內(nèi)容
? ? 僅POST方式含有(與請求頭有一個空行)
4.2.5?HttpServletRequest對象
????HttpServletRequest對象作用是用于獲取請求數(shù)據(jù)录平。
????核心的API:
(1)請求行:
????????request.getMethod(); 請求方式
????????request.getRequetURI() ??/ request.getRequetURL()請求資源
????????request.getProtocol()請求http協(xié)議版本
(2)請求頭:
????????request.getHeader("名稱") ??根據(jù)請求頭獲取請求值
????????request.getHeaderNames()獲取所有的請求頭名稱
(3)實(shí)體內(nèi)容:
????????request.getInputStream()獲取實(shí)體內(nèi)容數(shù)據(jù)
????????request.getParameter("參數(shù)名"); 根據(jù)參數(shù)名獲取參數(shù)值(注意麻车,只能獲取一個值的參數(shù))
????????request.getParameterValue("參數(shù)名“)缀皱;根據(jù)參數(shù)名獲取參數(shù)值(可以獲取多個值的參數(shù))
????????request.getParameterNames();? 獲取所有參數(shù)名稱列表?
4.3 Http響應(yīng)
4.3.1 響應(yīng)格式
HTTP/1.1 200 OK? ?--響應(yīng)行
Server: Apache-Coyote/1.1? ?--響應(yīng)頭(key-vaule)
Content-Length: 24?
Date: Fri, 30 Jan 2015 01:54:57 GMT
--一個空行
this is hello servlet!!!? ? ? --實(shí)體內(nèi)容
4.3.2 響應(yīng)行
(1)http協(xié)議版本
(2)狀態(tài)碼: 服務(wù)器處理請求的結(jié)果(狀態(tài))
????????常見的狀態(tài)碼:
????????????200: ?表示請求處理完成并完美返回
????????????302: ??表示請求需要進(jìn)一步細(xì)化。 404: ??表示客戶訪問的資源找不到动猬。
????????????500: ??表示服務(wù)器的資源發(fā)送錯誤。(服務(wù)器內(nèi)部錯誤)
(3)狀態(tài)描述
4.3.3? HttpServletResponse對象
????HttpServletResponse對象修改響應(yīng)信息:
(1)響應(yīng)行:
????????response.setStatus()? 設(shè)置狀態(tài)碼
(2)響應(yīng)頭:
????????response.setHeader("name","value")? 設(shè)置響應(yīng)頭
(3)實(shí)體內(nèi)容:
????????response.getWriter().writer();? 發(fā)送字符實(shí)體內(nèi)容
????????response.getOutputStream().writer()? 發(fā)送字節(jié)實(shí)體內(nèi)容
4.4 Https
4.4.1 與Http區(qū)別
(1)https 協(xié)議需要到 ca 申請證書钮莲,一般免費(fèi)證書較少彼水,因而需要一定費(fèi)用。
(2)http 是超文本傳輸協(xié)議凤覆,信息是明文傳輸,https 則是具有安全性的 ssl 加密傳輸協(xié)議(對稱加密)叛赚。
(3)http 和 https 使用的是完全不同的連接方式澡绩,用的端口也不一樣肥卡,前者是 80步鉴,后者是 443璃哟。
(4)http 的連接很簡單随闪,是無狀態(tài)的铐伴;HTTPS 協(xié)議是由 SSL+HTTP 協(xié)議構(gòu)建的可進(jìn)行加密傳輸当宴、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議户矢,比 http 協(xié)議安全。
4.4.2 工作原理
客戶端在使用HTTPS方式與 Web 服務(wù)器通信時有以下幾個步驟捌年。
⊙哟堋(1)客戶使用 https 的 URL 訪問 Web 服務(wù)器逆瑞,要求與 Web 服務(wù)器建立 SSL 連接获高。
∧钛怼(2)Web 服務(wù)器收到客戶端請求后摊趾,會將網(wǎng)站的證書信息(證書中包含公鑰)傳送一份給客戶端砾层。
「嘏凇(3)客戶端的瀏覽器與 Web 服務(wù)器開始協(xié)商 SSL 連接的安全等級侨糟,也就是信息加密的等級秕重。
∪茉拧(4)客戶端的瀏覽器根據(jù)雙方同意的安全等級汰具,建立會話密鑰留荔,然后利用網(wǎng)站的公鑰將會話密鑰加密聚蝶,并傳送給網(wǎng)站碘勉。
⊙槊摇(5)Web 服務(wù)器利用自己的私鑰解密出會話密鑰胜嗓。
〈侵荨(6)Web 服務(wù)器利用會話密鑰加密與客戶端之間的通信变过。
4.4.3 其他加密算法
4.4.3.1 AES
????高級加密標(biāo)準(zhǔn)(AES,Advanced Encryption Standard)為最常見的對稱加密算法(微信小程序加密傳輸就是用這個加密算法的)媚狰。對稱加密算法也就是加密和解密用相同的密鑰哈雏,具體的加密流程如下圖:
4.4.3.2 RSA
????RSA 加密算法是一種典型的非對稱加密算法裳瘪,它基于大數(shù)的因式分解數(shù)學(xué)難題彭羹,它也是應(yīng)用最廣泛的非對稱加密算法派殷。
????非對稱加密是通過兩個密鑰(公鑰-私鑰)來實(shí)現(xiàn)對數(shù)據(jù)的加密和解密的毡惜。公鑰用于加密经伙,私鑰用于解密。
4.4.3.3 CRC
????循環(huán)冗余校驗(yàn)(Cyclic Redundancy Check, CRC)是一種根據(jù)網(wǎng)絡(luò)數(shù)據(jù)包或電腦文件等數(shù)據(jù)產(chǎn)生簡短固定位數(shù)校驗(yàn)碼的一種散列函數(shù),主要用來檢測或校驗(yàn)數(shù)據(jù)傳輸或者保存后可能出現(xiàn)的錯誤张弛。它是利用除法及余數(shù)的原理來作錯誤偵測的。
4.4.3.4. MD5
????MD5 常常作為文件的簽名出現(xiàn)瞒大,我們在下載文件的時候,常常會看到文件頁面上附帶一個擴(kuò)展名為.MD5 的文本或者一行字符踢械,這行字符就是就是把整個文件當(dāng)作原數(shù)據(jù)通過 MD5 計(jì)算后的值内列,我們下載文件后话瞧,可以用檢查文件 MD5 信息的軟件對下載到的文件在進(jìn)行一次計(jì)算交排。兩次結(jié)果對比就可以確保下載到文件的準(zhǔn)確性埃篓。 另一種常見用途就是網(wǎng)站敏感信息加密,比如用戶名密碼部脚,支付簽名等等委刘。隨著 https 技術(shù)的普及钱雷,現(xiàn)在的網(wǎng)站廣泛采用前臺明文傳輸?shù)胶笈_罩抗,MD5 加密(使用偏移量)的方式保護(hù)敏感數(shù)據(jù)保護(hù)站點(diǎn)和數(shù)據(jù)安全套蒂。
參考:MD5算法分析及逆向詳解_天下布武之信長的專欄-CSDN博客_md5逆向
4.5 HttpClient工具
· 需要導(dǎo)入的依賴
<dependencies>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
????<groupId>javax.servlet</groupId>
????<artifactId>javax.servlet-api</artifactId>
????<version>3.1.0</version>
????<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
????<groupId>org.apache.httpcomponents</groupId>
????<artifactId>httpclient</artifactId>
????<version>4.3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
????<groupId>org.apache.commons</groupId>
????<artifactId>commons-lang3</artifactId>
????<version>3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
????<groupId>com.alibaba</groupId>
????<artifactId>fastjson</artifactId>
????<version>1.2.29</version>
</dependency>
</dependencies>
· HttpClient
/**
* 發(fā)送 post請求訪問本地應(yīng)用并根據(jù)傳遞參數(shù)不同返回不同結(jié)果
*/
public void post() {
????// 創(chuàng)建默認(rèn)的httpClient實(shí)例.
????CloseableHttpClient httpclient = HttpClients.createDefault();
????// 創(chuàng)建httppost
????HttpPost httppost = new HttpPost("http://localhost:8080/myDemo/Ajax/serivceJ.action");
????// 創(chuàng)建參數(shù)隊(duì)列
????List<NameValuePair> formparams = new ArrayList<NameValuePair>();
????formparams.add(new BasicNameValuePair("type", "house"));
????UrlEncodedFormEntity uefEntity;
????try {
????????uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
????????httppost.setEntity(uefEntity);
????????System.out.println("executing request " + httppost.getURI());
????????CloseableHttpResponse response = httpclient.execute(httppost);
? ? ? ? try {
????????????HttpEntity entity = response.getEntity();
????????????if (entity != null) {
????????????System.out.println("--------------------------------------");
????????????System.out.println("Response content: " + EntityUtils.toString(entity, "UTF-8"));
????????????System.out.println("--------------------------------------");
????????????}
???????} finally {
????????????response.close();
? ? ????}
????} catch (ClientProtocolException e) {
????????e.printStackTrace();
????} catch (UnsupportedEncodingException e1) {
????????e1.printStackTrace();
????} catch (IOException e) {
????????e.printStackTrace();
????} finally {
????????// 關(guān)閉連接,釋放資源
????????????try {
????????????????httpclient.close();
? ? ? ? ? ? ? } catch (IOException e) {
????????????????????e.printStackTrace();
????????????????}
????}
}
/**
* 發(fā)送 get請求
*/
public void get() {
????CloseableHttpClient httpclient = HttpClients.createDefault();
????try {
????????// 創(chuàng)建httpget.
????????HttpGet httpget = new HttpGet("http://www.baidu.com/");
????????System.out.println("executing request " + httpget.getURI());
????????// 執(zhí)行g(shù)et請求.
????????CloseableHttpResponse response = httpclient.execute(httpget);
????????try {
????????????// 獲取響應(yīng)實(shí)體
????????????HttpEntity entity = response.getEntity();
????????????System.out.println("--------------------------------------");
????????????// 打印響應(yīng)狀態(tài)
????????????System.out.println(response.getStatusLine());
????????????if (entity != null) {
????????????????// 打印響應(yīng)內(nèi)容長度
????????????????System.out.println("Response content length: " + entity.getContentLength());
????????????????// 打印響應(yīng)內(nèi)容
????????????????System.out.println("Response content: " + EntityUtils.toString(entity));
????????????}
????????????System.out.println("------------------------------------");
????????} finally {
????????????response.close();
????????}
????} catch (ClientProtocolException e) {
????????e.printStackTrace();
????} catch (ParseException e) {
????????e.printStackTrace();
????} catch (IOException e) {
????????e.printStackTrace();
????} finally {
????????// 關(guān)閉連接,釋放資源
????????try {
????????????httpclient.close();
????????????} catch (IOException e) {
????????????????e.printStackTrace();
????????}
????}
}
4.6 跨域解決方案
(1)ajax + 響應(yīng)頭 Access-control-Allow-origin
? ??????在當(dāng)前域名請求網(wǎng)站中撼嗓,默認(rèn)不允許通過ajax請求發(fā)送其他域名且警。報(bào)錯XMLHttpRequest cannot load 斑芜。
$.ajax({
????????type : 'post',
????????dataType : "text",
????????url : "http://a.a.com/a/FromUserServlet",
????????data : "userName=余勝軍&userAge=19",
????????success : function(msg) {
????????alert(msg);
????????}
});
????????后臺response添加header,response.setHeader("Access-Control-Allow-Origin", "*"); 支持所有網(wǎng)站
(2)JSONP
? ? ? ? 在ajax中加入JSONP格式傳輸醇王。
$.ajax({
????type : "POST",
????async : false,
????url : "http://a.a.com/a/FromUserServlet?userName=張三",
????dataType : "jsonp",//數(shù)據(jù)類型為jsonp?
????jsonp : "jsonpCallback",//服務(wù)端用于接收callback調(diào)用的function名的參數(shù)?
????success : function(data) {
????????alert(data.result);
????},
????error : function() {
????????alert('fail');
????}
});
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
????resp.setCharacterEncoding("UTF-8");
????// resp.setHeader("Access-Control-Allow-Origin", "*");
????String userName = req.getParameter("userName");
????String userAge = req.getParameter("userAge");
????System.out.println(userName + "----" + userAge+"---"+req.getMethod());
????try {
????????resp.setContentType("text/plain");
????????resp.setHeader("Pragma", "No-cache");
????????resp.setHeader("Cache-Control", "no-cache");
????????resp.setDateHeader("Expires", 0);
????????PrintWriter out = resp.getWriter();
????????JSONObject resultJSON = new JSONObject(); // 根據(jù)需要拼裝json
????????resultJSON.put("result", "content");
????????String jsonpCallback = req.getParameter("jsonpCallback");// 客戶端請求參數(shù)
????????out.println(jsonpCallback + "(" + resultJSON.toJSONString() + ")");// 返回jsonp格式數(shù)據(jù)
????????out.flush();
????????out.close();
????} catch (Exception e) {
????????// TODO: handle exception
????}
}
· 注意事項(xiàng)
? ? · 原理是,底層生成一個get請求發(fā)送根暑。
? ? · 缺點(diǎn):只支持get請求排嫌,不支持post請求
(3)網(wǎng)關(guān)系統(tǒng)
? ? ? ? 使用nginx進(jìn)行域名轉(zhuǎn)發(fā)
(4)使用后臺服務(wù)器轉(zhuǎn)發(fā)淳地,如HttpClient
? ? ? ? 不建議使用颇象,非常占寬帶遣钳。
5 單點(diǎn)登錄SSO
5.1 概念
????SSO英文全稱Single Sign On劝评,單點(diǎn)登錄蒋畜。SSO是在多個應(yīng)用系統(tǒng)中姻成,用戶只需要登錄一次就可以訪問所有相互信任的應(yīng)用系統(tǒng)佣渴。
例如訪問在網(wǎng)易賬號中心(http://reg.163.com/)登錄之后,訪問以下站點(diǎn)都是登錄狀態(tài):
· 網(wǎng)易直播http://v.163.com
· 網(wǎng)易博客http://blog.163.com
· 網(wǎng)易花田http://love.163.com
· 網(wǎng)易考拉https://www.kaola.com
· 網(wǎng)易Lofterhttp://www.lofter.com
5.2 架構(gòu)
(1)架構(gòu)說明
· 前臺站點(diǎn):需要登錄的站點(diǎn)
· SSO站點(diǎn)-登錄:提供登錄的頁面
· SSO站點(diǎn)-登出:提供注銷登錄的入口
· SSO服務(wù)-登出:提供登錄服務(wù)
· SSO服務(wù)-登錄狀態(tài):提供登錄狀態(tài)校驗(yàn)/登錄信息查詢的服務(wù)
· SSO服務(wù)-登出:提供用戶注銷登錄的服務(wù)
· 數(shù)據(jù)庫:存儲用戶賬戶信息
· 緩存:存儲用戶的登錄信息,通常使用Redis
·用戶登錄狀態(tài)的存儲和校驗(yàn)邏輯
(2)用戶登錄狀態(tài)的存儲和校驗(yàn)
????常見的Web框架對于Seesion的實(shí)現(xiàn)都是生成一個SessionId存儲在瀏覽器Cookie中鹃答。然后將Session內(nèi)容存儲在服務(wù)器端內(nèi)存中测摔。
????用戶登錄成功之后,生成AuthToken交給客戶端保存挟纱。如果是瀏覽器紊服,就保存在Cookie中欺嗤。如果是手機(jī)App就保存在App本地緩存中煎饼。 用戶在瀏覽需要登錄的頁面時腺占,客戶端將AuthToken提交給SSO服務(wù)校驗(yàn)登錄狀態(tài)/獲取用戶登錄信息
????對于登錄信息的存儲铡羡,建議采用Redis烦周,使用Redis集群來存儲登錄信息读慎,既可以保證高可用夭委,又可以線性擴(kuò)充株灸。同時也可以讓SSO服務(wù)滿足負(fù)載均衡/可伸縮的需求。
????AuthToken屹蚊,直接使用UUID/GUID即可汹粤,如果有驗(yàn)證AuthToken合法性需求玄括,可以將UserName+時間戳加密生成遭京,服務(wù)端解密之后驗(yàn)證合法性
5.3 用戶登錄
按照上圖船殉,用戶登錄后Authtoken保存在Cookie中利虫。 domian= test. com瀏覽器會將domain設(shè)置成 .test.com糠惫,這樣訪問所有*.test.com的web站點(diǎn)硼讽,都會將Authtoken攜帶到服務(wù)器端。然后通過SSO服務(wù)备燃,完成對用戶狀態(tài)的校驗(yàn)/用戶登錄信息的獲取并齐。
5.4 登錄信息獲取/登錄狀態(tài)校驗(yàn):
5.5 用戶登出時序圖
用戶登出時要做的事情很簡單:
· 服務(wù)端清除緩存(Redis)中的登錄狀態(tài)
· 客戶端清除存儲的AuthToken
5.6 跨域登錄/登出
????跨域要解決的問題,就是如何解決Cookie的跨域讀寫問題麻掸。
????解決跨域的核心思路就是:
· 登錄完成之后通過回調(diào)的方式脊奋,將AuthToken傳遞給主域名之外的站點(diǎn)膊存,該站點(diǎn)自行將AuthToken保存在當(dāng)前域下的Cookie中久又。
· 登出完成之后通過回調(diào)的方式,調(diào)用非主域名站點(diǎn)的登出頁面畏妖,完成設(shè)置Cookie中的AuthToken過期的操作。
·跨域登錄(主域名已登錄)
·跨域登錄(主域名未登錄)
·跨域登出
5.7 實(shí)現(xiàn)
03-shiro權(quán)限系統(tǒng)-單點(diǎn)登陸 - 簡書
6 常見問題總結(jié)
6.1?eclipse 開發(fā)web程序
????啟動tomcat服務(wù)器的時候。臨時目錄在你的工作區(qū)間workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\
? ? 用于臨時存放服務(wù)器編譯過后的jsp、servlet敬尺、字節(jié)碼文件砂吞、圖片之類的蜻直,相當(dāng)于緩存,不用每次都編譯赎瑰,提高速度餐曼。
6.2?Servlet里resp.getWriter().write("\n")不能換行
? ? servlet編程的時候使用 resp.getWriter().write("\n") 來換行源譬,一開始還覺得代碼沒有問題呀,也沒報(bào)錯养渴,太天真了厚脉,原來是要用html標(biāo)簽來換行傻工。
????使用resp.getWriter().write("< br/>") 完全ok鸯匹,不然resp.setContentType(“text/html”)是擺在那里吃素的嗎E古睢H咎肖粮!