まず、なぜセッションを生成する必要があります
httpプロトコル自体はステートレスで、クライアントはダウンロードサーバ、クライアントを要求する必要があるし、別の履歴情報を記録していないサーバーは、各要求は独立しています。
それステートレスのはなぜですか?ブラウザとサーバがsockeソケット通信を使用しているため、サーバがブラウザに復帰した後の結果は、それが現在のソケットのリンクを閉じて要求し、サーバが終了したページオブジェクトのページを処理した後に破棄されます。
しかし、多くのシナリオで(など、パスワード、閲覧履歴を、覚えておいてください)(ログイン、などあれば)が正しく動作するようにユーザー状態のWebアプリケーションを維持するために必要な、または便利な提供、機能の状態を維持することが非常に重要です。したがって、技術のHTTPリンクの状態が保持されているWebアプリケーション開発:1つのクッキーの技術は、他の技術的なセッションがあります。
二、どのような役割セッションは、どのように生産し、役割を再生するには
理解するために、セッションはクッキー、クッキーとセッションとの関係であるかを把握する必要があります。
1.クッキーとは何ですか
クッキーの技術は、クライアントの状態は溶液中に残っているのhttp、クッキーはクライアントに保存されているテキストファイルにサーバーからクライアントへの特別なメッセージ、および情報であり、その後、クライアントがサーバにリクエストを送信する際に、各時間で持って来ますこれらの特別な情報。
2、クッキー生成
サイトを訪問すると、ブラウザのクッキー時に初めてのユーザーをサポートしている場合、ユーザーは、以下を含む、サーバに提出、ユーザー名などの個人情報を提供します。そして、サーバーは、対応するハイパーテキストながら、見返りに戻ってクライアントに送信しますもちろん、そのような情報は、HTTPレスポンスボディ(レスポンスボディ)に格納されていない個人情報があるが、HTTPレスポンスヘッダ(レスポンスヘッダ)に格納され、クライアントブラウザがサーバからの応答を受信した場合、ブラウザの意志この情報は、単一の場所に格納されます。
ハードディスクに保存されたクッキーは、別のブラウザシェアリングの間にすることはできません、このような二IEのウィンドウと同じブラウザで異なるプロセス間で共有することができます。ブラウザ内に格納された各クッキーの位置など、同じではないからです
クローム下的クッキー放在:C:\ Users \ユーザーsharexie \のAppData \ローカル\ Googleの\クローム\ユーザーデータ\デフォルト\キャッシュ
Firefoxの下に置かれたクッキー:C:\ Users \ユーザーsharexie \のAppData \ローミング\ Mozilla \ Firefox \ Profiles \とtq2hit6m.default \ cookies.sqlite(最後から二番目のファイル名はランダムなファイル名です)
すなわち下に置かれたクッキー:C:\ユーザーは、管理者\のAppData \ローミング\マイクロソフト\ Windowsの\クッキーを\
3、クッキーの内容、範囲および有効性
クッキーは含まれるべきである:名前、値、有効期限、パスおよびドメインを。Pathフィールドは、一緒にクッキーの有効範囲を構成します。
あなたは有効期限を設定しない場合は、ブラウザセッションの間、クッキーの有効期間は、単にブラウザウィンドウを閉じ、クッキーは、ブラウザのセッションクッキーの有効期間は、セッションクッキーと呼ばれているように、姿を消しました。セッションクッキーは、一般的にハードに保存されているが、メモリに保存されていません。あなたは有効期限を設定した場合、ブラウザのクッキーを閉じた後、再びブラウザを開き、お使いのハードドライブに保存されます有効期限が設定を超えるまで、これらのクッキーは有効なまま。
4、クッキーを使用する方法
クッキーの使用はバックグラウンドで自動的に特定の原則に従い、ブラウザによってサーバーに送信されます。
クライアントは、セカンダリサーバに要求を送信すると、ブラウザが保存されているすべてのクッキーをチェックし、クッキースコープは、リソースの述べた場所が要求されます以上であれば、要求リソースのHTTPリクエストに付加されているクッキーを置きます頭がサーバーに送信されます。この技術では、クッキーは、サーバは、クライアントブラウザからの要求を受信した後、分析を動的に最終顧客に対応するコンテンツを生成するために、クライアント固有の情報を得られたクッキーリクエストヘッダに格納することができます。一般的に、我々はあなたがサイトを訪問し、次回の繰り返しで退屈ログインする必要はありません、このオプション「私を忘れないでください」のログイン画面からの多くのサイトを見て、あなたがそれをチェックすると、後でログオンすることができますアクション、そしてこの機能は、クッキーによって実装されます。
5.セッションとは何ですか
一般的な答えと呼ばれるセッションは、テクニカルセッション状態は、サーバーの状態を維持することであるhttpサーバソリューション、に維持されています。私たちは、運動中に、クライアントブラウザとサーバがセッションと呼ばれている間の一連の相互作用を置くことができます。クライアントが保持するために使用された情報を保存するストレージスペースを開放するためのサーバです。そのため、セッションは完全なトランザクションへの一連のアクションで、クライアントとサーバーの相互作用を可能にするステートレスなHTTPプロトコルサーバーソリューションの問題を解決することです。
6、セッションが作成され
そして、セッションでときにそれを作成するには?もちろん、セッションを作成したり、プログラムを実行しているサーバーの間に作成され、異なる言語のアプリケーションにはさまざまな方法があります。
クライアントが初めてサーバーを要求すると、サーバー側のプログラムは、このようなステートメントHttpServletRequest.getSession(true)を呼び出す時は、サーバはクライアントのセッションを作成し、セッションIDがのために特別なアルゴリズムによって計算されますセッションオブジェクトを識別します。
セッションは、サーバ(メモリに格納されたセッションクラスStandardManagerによってTomcatサーバ)のメモリに格納され、ファイル、データベース、メモリキャッシュに保持されてもよい、Redisのが好き。クライアントは、セッションIDクッキーを保存しますが、セッションを保存しないように。
ブラウザを閉じると、セッションは削除されませんが、場合にのみ、タイムアウト、プログラムは(HttpSession.invalidateを呼び出し)とサーバプログラムを閉じ削除されます。
7、Tomcatはセッション中に作成しました
ManagerBaseは抽象クラスで、すべてのセッション管理ツールの基本クラスで、である、すべての実現にセッション管理クラスが保護されたメソッドを持って、このクラスを継承する必要があり、このメソッドは、メソッドのSessionID価値創造であります:
8、クッキーとセッションの関係
クッキーとセッションプログラムは、クライアントとサーバーに属するが、それはセッションのid値が生成されます場合は、クライアントのクッキーの依存関係上、サーバー側の実行セッションのメカニズムでサーバーのセッションを実現するものの、id値がクライアントに送信されますクライアントIDは、すべての要求は、サーバーへのヘッドのhttpリクエストにこの値を入れて、保存されたクライアントのid値、そのクッキーコンテナを保存するので、我々は完全にブラウザのクッキーを禁止するとき時間は、セッションサーバが正常に動作しません。
第三に、分散システムセッション共有
セッションは、実際に問題を共有するサーバのクラスタで、コースを保つために、クライアントとサーバー間の通信セッション、全体の記録セッションセッションは、基本的な情報を伝えます。しかし、クラスタ環境では、サービスAにアクセスするための最初の時間のため、クライアントは、サービスが応答AセッションIDを返し、地元のクッキーに保存されていることを前提としています。第二には、サービスにアクセスし、サービスBにアクセスすることをオンにしないで サービスBにアクセスするときに、セッションIDで見つかりませ該当するデータがないため、すでにそこにセッションIDのクライアントのクッキーは、そう、セッションIDは、リクエストヘッダ、及びサービスBに追加されますので、それは新しいが作成されますので、セッションIDとクライアントに応答を返します。この問題は、セッションを共有できない原因となりました。
例えば、SpringCloudプロジェクトでは、2つの異なるポートで、それぞれのサービスを起動し、その後、したがって、2台のサーバのクラスタを形成し、ユーレカサーバーに登録し、リボンの負荷分散ポリシーは、ポーリングポリシーに設定されています。サーバに処理を要求:
@RestController パブリック クラスTestSessionController { @value( "はserver.portの$ {}" ) プライベート整数projectPort; // アイテムポート @RequestMapping( "/のCreateSession" ) 公共の文字列のCreateSession(HttpSessionのセッション、文字列名){ にsession.setAttribute( "名前" ;、名前) リターン "+ projectPort +"現在のセッションID :! "+ session.getId():現在のプロジェクトポート" +" セッションの成功への" ; } (RequestMapping @ " /のgetSessionを") パブリック文字列のgetSession(HttpSessionのセッション){ リターン"現在のプロジェクトポート:" + projectPort + "現在のセッションID:" + session.getId()+ " 名前取得する:" + session.getAttribute( "名前" ); } }
私たちは、http、名前でセッションに直接ポーリングメカニズムの最初の預金を経由してアクセス://www.hello.com/createSession名= AAA?
現在のプロジェクトのポート:8081現在のセッションID:セッションの成功への0F20F73170AE6780B1EC06D9B06210DB!
我々は、デフォルトのポーリングメカニズムを使用しているので、次の訪問は、ポート8080確かである、我々は次の値に直接アクセスhttp://www.hello.com/getSessionだけ預金を持っています
現在のプロジェクトのポート:8080現在のセッションID:C6663EA93572FB8DAE27736A553EAB89は名前を取得:ヌル
8080ポートと異なるセッションIDと8081個のポートも私たちの店で何の価値を発見した、と。
この時間なので、私たちは8081で名前になる前に、その後、サーバポート8080がセッションに格納されていない、ポーリングメカニズムサーバポート8081です。
訪問を続け、AAA:ます:http://www.hello.com/getSessionその後、我々は我々が名前に得ることができるかどうかを確認するために8081のポートサービスを再訪します
現在のプロジェクトのポート:8081現在のセッションID:005EE6198C30D7CD32FBD8B073531347名を取得:ヌル
同様のポートをオフに格納される前にいなくても8081ポート8080、だけではないことがわかりました。
実際には、検出ポートは8081で第三の訪問をSessionIDの時点で、我々はポートを訪問する二度目の今回は8080クライアントでは見られない、クッキーに8080サーバのポート8081に行くことになっ訪れたので、同じではありませんクライアントのセッションとセッションIDの応答を再作成した後、クライアントには、その後のセッションID二の訪問の第三の訪問は、彼らが見つけることができない見つけました8081、前のセッションIDを交換するcookidを維持しながら、彼らが作成しました。サイクルを繰り返してきた、2台のサーバーが常に他の側はセッションIDを生成されます、クラスタのセッション共有でセッションID自身の世代を取得。
第四に、解決するためにどのように共有の問題セッション
一般的なセッション共有プログラムは次のように要約されます。
- 完了するために、クッキーを使用して、(明らかに危険な操作は信頼できません)
- ポリシーnginxのバインディングIPを使用して、同じIPが唯一の(負荷分散をサポートしていません)指定したのと同じマシンにアクセスすることができます
- データベースの同期セッション(非効率的)を使用します
- Tomcatのセッション組み込みの同期を使用して(同期は遅延が発生する場合があります)
- 代わりに、トークンのセッションを使用します
- 春のセッション+ Redisのは、実装に使用
クッキーを達成するための1、
、ルートドメインのサイトの下のすべてのセカンドレベルドメインにアクセスするためにブラウザを使用します。これは(.host.comなど)ルートドメインの下に統一さ植栽、クッキーの道に、直列化した後、セッション情報システムユーザーを暗号化する原則的な方法ですこれに対応するクッキーのドメイン名のすべての内容の伝達特性がするとき、セッションCookieの有効ユーザーは、複数のサービス間のアクセスを共有しました。
この方式の利点は、追加のサーバ・リソースなしで、不利である小区間のユーザ情報を格納することができるだけによって制限ヘッダ長httpプロトコルが、セッション内容のクッキーは、安全な暗号化が必要(例:等DES、RSAを使用して平文の暗号化と復号化し、次にMD5、SHA-1アルゴリズムのセキュリティ認証)、それはながら帯域幅の一定量を取り、ブラウザがHTTPヘッダで送信現在のドメイン名の要求に応じて接続されたローカルクッキー内の任意のリソースになるので、サーバーに、最も重要なのは、セキュリティ上のリスクがあります。
2、政策nginxの結合IPを使用して
このip_hashで単にnginxの構成、その上に、このアプローチの欠点も明らかである、nginxのは、ロード・バランシングをサポートしていないバインディングIPを設定します。特定の参照ブログ:https://www.cnblogs.com/ywb-articles/p/10686673.html
図3に示すように、データベース同期セッションを使用して
これMySQLは、例えば、各セッションでは、データベースにデータを保存します。このプログラムは非常に実現可能である、多くの開発者は、このアプローチを使用しています。しかし、その欠点は、その除去セッションロジックを達成する必要が同時にあまりにも簡単にするとき、タイミングデータテーブルから更新するレコードのセッションを削除する一方で、同時読み取りおよび書き込みセッション機能は、MySQLデータベース、大きな圧力のデータベースのパフォーマンスに依存していることですあなたは、テーブルの行レベルのロックのエンジンを選択することができますが、テーブルロックは、発生しますが、このプログラムでは、多くの人々が最適な解決策ではありません。
図4に示すように、代わりのセッショントークンを使用して
ここでトークン一般セッションデータ共有に置き換えるためにそれを使用し、JSONウェブトークンです。
トークンベースの認証方法を使用して、サーバーに記録したログオンは、ユーザーを保存する必要はありません。おそらくプロセスはこれです:
1、サーバーのログを記録するためのクライアントのユーザー名とパスワード;
2、クライアントの本人確認にサーバー;
3、サーバーユーザー生成トークンは、クライアントに返さ;
4、クライアントトークンは、一般的な、ローカルのブラウザに保存されましたクッキーに保存され、
5、クライアントが要求を送信し、我々はトークンを携帯する必要があります。
6、サーバは要求を受信した後、最初のトークンは、その後、データを返されたことを確認してください。
上記のように、左のトークンの実装で、フォトセッションの実装があり、流れは実質的に均一です。特定の参照:https://www.cnblogs.com/lightzone/p/9749076.html
一意の識別子のユーザーIDに基づいてサーバにアクセスする最初のブラウザでは、サーバーは、このような一般的に使用されるHMAC-SHA256アルゴリズムとしていくつかのアルゴリズムを介して、上を通過した後、トークンを生成するためにキーを追加し、このトークンの後にエンコードBASE64に目を通すだろうクライアントに、クライアントトークンが保存され、トークンを使用して次の要求、サーバは要求を受信し、採用した場合、確認するために、同じアルゴリズムとキートークンを使用します、ではなくて、トラフィックの操作を実行情報を通じて返されます。
利点:
- ステートレス、スケーラブル:クライアントによって格納されたトークンはステートレスで、かつ拡張することができますで。この格納状態と非セッション情報に基づいて、ロードバランサは、別のサーバ上のサービスからのユーザ情報を送信することができます。
- セキュリティ:リクエストトークンのクッキーを送信する代わりにCSRFを送信するには、(クロスサイトリクエストフォージェリ)を防止することができます。
- サードパーティのサービスへのインタフェースを提供することができる:トークンを使用する場合は、サードパーティのアプリケーションへのオプションの権限を提供しています。
- マルチプラットフォームクロスドメイン
アプリケーションやサービスを展開すると、すべての種類のデバイスやアプリケーションのすべての種類の介入。私たちのバックエンドサーバーAPI a.comはデータのみを提供し、静的リソースは、サーバーのCDN b.comに保存されている場合。同一生成元ポリシーの制限は、ブラウザがブロックされているトリガーに起因して、我々は、a.comからb.com次のリソースを要求したとき。
我们通过CORS(跨域资源共享)标准和token来解决资源共享和安全问题。
举个例子,我们可以设置b.com的响应首部字段为:
Access-Control-Allow-Origin: http://a.com
Access-Control-Allow-Headers: Authorization, X-Requested-With, Content-Type, Accept
Access-Control-Allow-Methods: GET, POST, PUT,DELETE
第一行指定了允许访问该资源的外域 URI。
第二行指明了实际请求中允许携带的首部字段,这里加入了Authorization,用来存放token。
第三行用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。
然后用户从a.com携带有一个通过了验证的token访问B域名,数据和资源就能够在任何域上被请求到。
5、使用tomcat内置的session同步
5.1 tomcat中server.xml直接配置
<!-- 第1步:修改server.xml,在Host节点下添加如下Cluster节点 --> <!-- 用于Session复制 --> <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8"> <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true" /> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <Membership className="org.apache.catalina.tribes.membership.McastService" address="228.0.0.4" port="45564" frequency="500" dropTime="3000" /> <!-- 这里如果启动出现异常,则可以尝试把address中的"auto"改为"localhost" --> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="auto" port="4000" autoBind="100" selectorTimeout="5000" maxThreads="6" /> <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"> <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender" /> </Sender> <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector" /> <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor" /> </Channel> <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter="" /> <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve" /> <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" tempDir="/tmp/war-temp/" deployDir="/tmp/war-deploy/" watchDir="/tmp/war-listen/" watchEnabled="false" /> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener" /> </Cluster>
5.2 修改web.xml
web.xml中需加入<distributable/> 以支持集群。
<distributable/>
5、Spring-Session+Redis实现
Spring提供了一个解决方案:Spring-Session用来解决两个服务之间Session共享的问题。
5.1 在pom.xml中添加相关依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- spring session 与redis应用基本环境配置,需要开启redis后才可以使用,不然启动Spring boot会报错 --> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
5.2 修改application.properties全局配置文件(本地要开启redis服务)
spring.redis.database=0
spring.redis.host=localhost
spring.redis.port=6379
#spring.redis.password=
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-wait=-1
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.min-idle=8
spring.redis.timeout=10000
5.3 在代码中添加Session配置类
/** * 这个类用配置redis服务器的连接 * maxInactiveIntervalInSeconds为SpringSession的过期时间(单位:秒) */ @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800) public class SessionConfig { // 冒号后的值为没有配置文件时,制动装载的默认值 @Value("${redis.hostname:localhost}") private String hostName; @Value("${redis.port:6379}") private int port; // @Value("${redis.password}") // private String password; @Bean public JedisConnectionFactory jedisConnectionFactory() { RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); redisStandaloneConfiguration.setHostName(hostName); redisStandaloneConfiguration.setPort(port); // redisStandaloneConfiguration.setDatabase(0); // redisStandaloneConfiguration.setPassword(RedisPassword.of("123456")); return new JedisConnectionFactory(redisStandaloneConfiguration); } @Bean public StringRedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { return new StringRedisTemplate(redisConnectionFactory); } }
5.4 初始化Session配置
/** * 初始化Session配置 */ public class RedisSessionInitializer extends AbstractHttpSessionApplicationInitializer { public RedisSessionInitializer() { super(RedisSessionConfig.class); } }
Spring-Sesion实现的原理
当Web服务器接收到请求后,请求会进入对应的Filter进行过滤,将原本需要由Web服务器创建会话的过程转交给Spring-Session进行创建。Spring-Session会将原本应该保存在Web服务器内存的Session存放到Redis中。然后Web服务器之间通过连接Redis来共享数据,达到Sesson共享的目的。