<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          redis實(shí)現(xiàn)單點(diǎn)登錄系統(tǒng)

          共 44360字,需瀏覽 89分鐘

           ·

          2021-05-25 21:08

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          76套java從入門(mén)到精通實(shí)戰(zhàn)課程分享

          單點(diǎn)登錄介紹 
          SSO英文全稱Single Sign On,單點(diǎn)登錄。SSO是在多個(gè)應(yīng)用系統(tǒng)中,用戶只需要登錄一次就可以訪問(wèn)所有相互信任的應(yīng)用系統(tǒng)。它包括可以將這次主要的登錄映射到其他應(yīng)用中用于同一個(gè)用戶的登錄的機(jī)制。它是目前比較流行的企業(yè)業(yè)務(wù)整合的解決方案之一。

          為什么要單點(diǎn)登錄

          在平常寫(xiě)案例的時(shí)候,如果只有一個(gè)web工程。如果要訪問(wèn)用戶相關(guān)信息,那么我們通常會(huì)寫(xiě)個(gè)攔截器,從session中看能不能取到用戶信息,如果不能那么需要返回到登錄頁(yè)面,用戶登錄后將用戶信息保存到session中,那么在此訪問(wèn)用戶中心就沒(méi)問(wèn)題了。然后這種做法在一個(gè)web工程中是沒(méi)問(wèn)題的。 

          如果系統(tǒng)是分布式的情況呢,比如拿商城來(lái)說(shuō),它是一個(gè)分布式的,什么會(huì)員模塊,商品管理模塊,購(gòu)物車(chē)模塊,訂單模塊等。如果還用上面那張驗(yàn)證方式的話,訪問(wèn)a模塊的話發(fā)現(xiàn)沒(méi)登錄,然后跳轉(zhuǎn)頁(yè)面登錄了,信息存入session中,如果下次訪問(wèn)的是b模塊,由于模塊都是存在于不同的服務(wù)器session中肯定沒(méi)有登錄用戶信息,那么肯定是訪問(wèn)不通過(guò)要求重新登錄。而且,為了解決高并發(fā)還得進(jìn)行集群,即使是兩次訪問(wèn)同一模塊,也有可能訪問(wèn)的是集群中的另外一臺(tái)服務(wù)器,這樣就存在多次要求登錄的問(wèn)題。 

          解決:我們可以整個(gè)Session服務(wù)器(SSO系統(tǒng))專門(mén)用來(lái)處理登錄問(wèn)題,這樣用戶每次訪問(wèn)用戶中心的時(shí)候都來(lái)該服務(wù)器判斷用戶有沒(méi)有登錄,如果登錄了放行,沒(méi)有登錄就跳轉(zhuǎn)到登錄頁(yè)面,登錄后將用戶信息保存到Session服務(wù)器中,我們需要用redis來(lái)模擬從前的session,以前用戶信息存入redis中,這里我們就將用戶信息存入redis中。

          那么怎么存,用什么做key什么做value呢?在一個(gè)web工程的時(shí)候,用戶信息存入session是這樣設(shè)置的session.setArrtribute(“admin”,user),獲取則是session.getAttribute(“admin”);

          很自然的想到用用戶id做為key,但是不要這樣做,因?yàn)檫@樣是不能區(qū)分不同的連接的,比如你在A電腦登錄了tom這個(gè)賬號(hào),信息存入redis中了且key為tom的id。然后換個(gè)電腦B,但是不直接登錄卻直接訪問(wèn)用戶中心(比如訂單結(jié)算),那么能不能訪問(wèn)呢?是可以訪問(wèn)的,因?yàn)閞edis中存了這么個(gè)鍵值對(duì)啊,然后就能取出來(lái)用戶信息說(shuō)明已經(jīng)登錄了,所以肯定給訪問(wèn)。但是這樣是不合理的,不應(yīng)該換電腦登錄還能直接訪問(wèn)用戶中心。 
          用一個(gè)web工程使用tomcat服務(wù)器的時(shí)候,tomcat是怎么區(qū)分不同連接的呢,實(shí)際上每次獲取session的時(shí)候tomcat會(huì)生成一個(gè)JSESSIONID的作為標(biāo)識(shí),然后返回給瀏覽器存到Cookie里面,下次再訪問(wèn)的時(shí)候會(huì)帶著這個(gè)JSESSIONID來(lái)訪問(wèn),然后tomcat拿到這個(gè)標(biāo)識(shí)會(huì)去尋找對(duì)應(yīng)的session,再?gòu)闹腥〕鲇脩粜畔ⅲ@樣如果換電腦B了,B瀏覽器里面是沒(méi)有這個(gè)JSESSIONID的,那么在服務(wù)端也找不到對(duì)應(yīng)的session更別說(shuō)取到用戶信息了,所以要求重新登錄。 
          既然是打算用redis來(lái)模擬session,那么也可以這樣做。用戶每次登錄的時(shí)候都會(huì)生成一個(gè)唯一的表示token,用它來(lái)作為key,用戶信息作為value,然后將token存到Cookie里面返給瀏覽器。用戶下次 
          訪問(wèn)用戶中心的時(shí)候,從Cookie里面取token,再用token從redis中取用戶信息,來(lái)判斷是否允許訪問(wèn)用戶中心。 
          這樣做,只要用戶換電腦登錄了,那么Cookie里面就沒(méi)有這個(gè)token就查不到用戶信息所以必須要重新登錄了,分析到這基本上也能做了。 

          前期準(zhǔn)備好的jar包 
          e3-common(jar)里面存放的是一些工具類 
          e3-manager-pojo(jar)里面存放的是mybatis逆向工程生成的Mapper和映射文件。 
          1、SSO工程搭建 
          e3-sso(pom) 
          |–e3-sso-interface(jar) 
          |–e3-sso-service(war) 
          e3-sso-web(war)

          e3-sso需要引入的jar包 
          pom.xml中

          <groupId>cn.e3mall</groupId>
            <artifactId>e3-sso</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <packaging>pom</packaging>
            <modules>
              <module>e3-sso-interface</module>
              <module>e3-sso-service</module>
            </modules>
            <dependencies>
              <dependency>
                  <groupId>cn.e3mall</groupId>
                <artifactId>e3-common</artifactId>
                <version>0.0.1-SNAPSHOT</version>
              </dependency>
            </dependencies>
            <!-- 配置tomcat插件 -->
            <build>
              <plugins>
                  <plugin>
                      <!-- 配置Tomcat插件 -->
                      <groupId>org.apache.tomcat.maven</groupId>
                      <artifactId>tomcat7-maven-plugin</artifactId>
                      <configuration>
                          <path>/</path><!-- 表示訪問(wèn)時(shí)候不帶工程名的 -->
                          <port>8087</port>
                      </configuration>
                  </plugin>
              </plugins>
            </build>

          e3-sso-interface引入jar包 
          pom.xml中

          <parent>
              <groupId>cn.e3mall</groupId>
              <artifactId>e3-sso</artifactId>
              <version>0.0.1-SNAPSHOT</version>
            </parent>
            <artifactId>e3-sso-interface</artifactId>
            <dependencies>
              <dependency>
                  <groupId>cn.e3mall</groupId>
              <artifactId>e3-manager-pojo</artifactId>
              <version>0.0.1-SNAPSHOT</version>
              </dependency>
            </dependencies>

          e3-sso-service引入jar包 
          pom.xml中

          <parent>
              <groupId>cn.e3mall</groupId>
              <artifactId>e3-sso</artifactId>
              <version>0.0.1-SNAPSHOT</version>
            </parent>
            <artifactId>e3-sso-service</artifactId>
            <packaging>war</packaging>
            <dependencies>
              <dependency>
                  <groupId>cn.e3mall</groupId>
                  <artifactId>e3-manager-dao</artifactId>
                  <version>0.0.1-SNAPSHOT</version>
              </dependency>
              <dependency>
                  <groupId>cn.e3mall</groupId>
              <artifactId>e3-sso-interface</artifactId>
              <version>0.0.1-SNAPSHOT</version>
              </dependency>
              <!-- Spring -->
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-context</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-beans</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-webmvc</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-jdbc</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-aspects</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-jms</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-context-support</artifactId>
              </dependency>
              <!-- dubbo相關(guān) -->
                  <dependency>
                      <groupId>com.alibaba</groupId>
                      <artifactId>dubbo</artifactId>
                      <exclusions>
                          <exclusion>
                              <groupId>org.springframework</groupId>
                              <artifactId>spring</artifactId>
                          </exclusion>
                          <exclusion>
                              <groupId>org.jboss.netty</groupId>
                              <artifactId>netty</artifactId>
                          </exclusion>
                      </exclusions>
                  </dependency>
                  <dependency>
                      <groupId>org.apache.zookeeper</groupId>
                      <artifactId>zookeeper</artifactId>
                  </dependency>
                  <dependency>
                      <groupId>com.github.sgroschupf</groupId>
                      <artifactId>zkclient</artifactId>
                  </dependency>
            </dependencies>

          spring配置文件1中:配置數(shù)據(jù)源以及spring與mybatis整合

          <!-- 數(shù)據(jù)庫(kù)連接池 -->
              <!-- 加載配置文件 -->
              <context:property-placeholder location="classpath:conf/*.properties" />
              <!-- 數(shù)據(jù)庫(kù)連接池 -->
              <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
                  destroy-method="close">
                  <property name="url" value="${jdbc.url}" />
                  <property name="username" value="${jdbc.username}" />
                  <property name="password" value="${jdbc.password}" />
                  <property name="driverClassName" value="${jdbc.driver}" />
                  <property name="maxActive" value="10" />
                  <property name="minIdle" value="5" />
              </bean>
              <!-- 讓spring管理sqlsessionfactory 使用mybatis和spring整合包中的 -->
              <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
                  <!-- 數(shù)據(jù)庫(kù)連接池 -->
                  <property name="dataSource" ref="dataSource" />
                  <!-- 加載mybatis的全局配置文件 -->
                  <property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
              </bean>
              <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
                  <property name="basePackage" value="cn.e3mall.mapper" />
              </bean>

          spring配置文件2中:配置事務(wù)

          <!-- 事務(wù)管理器 -->
              <bean id="transactionManager"
                  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                  <!-- 數(shù)據(jù)源 -->
                  <property name="dataSource" ref="dataSource" />
              </bean>
              <!-- 通知 -->
              <tx:advice id="txAdvice" transaction-manager="transactionManager">
                  <tx:attributes>
                      <!-- 傳播行為 -->
                      <tx:method name="save*" propagation="REQUIRED" />
                      <tx:method name="insert*" propagation="REQUIRED" />
                      <tx:method name="add*" propagation="REQUIRED" />
                      <tx:method name="create*" propagation="REQUIRED" />
                      <tx:method name="delete*" propagation="REQUIRED" />
                      <tx:method name="update*" propagation="REQUIRED" />
                      <tx:method name="find*" propagation="SUPPORTS" read-only="true" />
                      <tx:method name="select*" propagation="SUPPORTS" read-only="true" />
                      <tx:method name="get*" propagation="SUPPORTS" read-only="true" />
                  </tx:attributes>
              </tx:advice>
              <!-- 切面 -->
              <aop:config>
                  <aop:advisor advice-ref="txAdvice"
                      pointcut="execution(* cn.e3mall.sso.service..*.*(..))" />
              </aop:config>

          spring配置文件3中配置redis

          <!-- 連接redis單機(jī)版 -->
              <bean id="jedisClientPool" class="cn.e3mall.common.jedis.JedisClientPool">
                  <property name="jedisPool" ref="jedisPool"></property>
              </bean>
              <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
              <!-- 一定要用name,構(gòu)造方法太多用index容易錯(cuò) -->
                  <constructor-arg name="host" value="192.168.25.128"/>
                  <constructor-arg name="port" value="6379"/>
              </bean>

          spring配置文件中配置:發(fā)布服務(wù),組件掃描,寫(xiě)完服務(wù)再給出

          web.xml中

          <!-- 加載spring容器 -->
              <context-param>
                  <param-name>contextConfigLocation</param-name>
                  <param-value>classpath:spring/applicationContext*.xml</param-value>
              </context-param>
              <listener>
                  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
              </listener>

          配置文件db.properties中配置數(shù)據(jù)庫(kù)連接

          jdbc.driver=com.mysql.jdbc.Driver
          jdbc.url=jdbc:mysql://localhost:3306/e3mall_32?characterEncoding=utf-8
          jdbc.username=root
          jdbc.password=123456

          resource.properties中設(shè)置token過(guò)期時(shí)間

          SESSION_EXPIRE=1800

          e3-sso-web引入jar包 
          pom.xml中

          <groupId>cn.e3mall</groupId>
            <artifactId>e3-sso-web</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <packaging>war</packaging>
            <dependencies>
              <dependency>
                <groupId>cn.e3mall</groupId>
                  <artifactId>e3-sso-interface</artifactId>
                  <version>0.0.1-SNAPSHOT</version>   
              </dependency>
              <!-- JSP相關(guān) -->
              <dependency>
                  <groupId>jstl</groupId>
                  <artifactId>jstl</artifactId>
              </dependency>
              <dependency>
                  <groupId>javax.servlet</groupId>
                  <artifactId>servlet-api</artifactId>
                  <scope>provided</scope>
              </dependency>
              <dependency>
                  <groupId>javax.servlet</groupId>
                  <artifactId>jsp-api</artifactId>
                  <scope>provided</scope>
              </dependency>
              <!-- Spring -->
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-context</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-beans</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-webmvc</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-jdbc</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-aspects</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-jms</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-context-support</artifactId>
              </dependency>
              <!-- dubbo相關(guān) -->
                  <dependency>
                      <groupId>com.alibaba</groupId>
                      <artifactId>dubbo</artifactId>
                      <exclusions>
                          <exclusion>
                              <groupId>org.springframework</groupId>
                              <artifactId>spring</artifactId>
                          </exclusion>
                          <exclusion>
                              <groupId>org.jboss.netty</groupId>
                              <artifactId>netty</artifactId>
                          </exclusion>
                      </exclusions>
                  </dependency>
                  <dependency>
                      <groupId>org.apache.zookeeper</groupId>
                      <artifactId>zookeeper</artifactId>
                  </dependency>
                  <dependency>
                      <groupId>com.github.sgroschupf</groupId>
                      <artifactId>zkclient</artifactId>
                  </dependency>

            </dependencies>
            <!-- 配置tomcat插件 -->
            <build>
              <plugins>
                  <plugin>
                      <!-- 配置Tomcat插件 -->
                      <groupId>org.apache.tomcat.maven</groupId>
                      <artifactId>tomcat7-maven-plugin</artifactId>
                      <configuration>
                          <path>/</path><!-- 表示訪問(wèn)時(shí)候不帶工程名的 -->
                          <port>8088</port>
                      </configuration>
                  </plugin>
              </plugins>
            </build>

          spring配置文件中除了組件掃描之外還要引用服務(wù),但是Service層還沒(méi)發(fā)布服務(wù),所以待會(huì)給出,另外還需要將jsp頁(yè)面跟靜態(tài)文件都引入到項(xiàng)目的WEB-INF和webaap下。

          web.xml中

          <!-- 解決post亂碼 -->
              <filter>
                  <filter-name>CharacterEncodingFilter</filter-name>
                  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
                  <init-param>
                      <param-name>encoding</param-name>
                      <param-value>utf-8</param-value>
                  </init-param>
              </filter>
              <filter-mapping>
                  <filter-name>CharacterEncodingFilter</filter-name>
                  <url-pattern>/*</url-pattern>
              </filter-mapping>


              <!-- springmvc的前端控制器 -->
              <servlet>
                  <servlet-name>e3-manager</servlet-name>
                  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
                  <!-- contextConfigLocation不是必須的, 如果不配置contextConfigLocation, springmvc的配置文件默認(rèn)在:WEB-INF/servlet的name+"-servlet.xml" -->
                  <init-param>
                      <param-name>contextConfigLocation</param-name>
                      <param-value>classpath:spring/springmvc.xml</param-value>
                  </init-param>
                  <load-on-startup>1</load-on-startup>
              </servlet>
              <servlet-mapping>
                  <servlet-name>e3-manager</servlet-name>
                  <!-- 偽靜態(tài)化,搜索引擎優(yōu)化,搜索引擎會(huì)先找靜態(tài)頁(yè)面 -->
                  <url-pattern>/</url-pattern>
              </servlet-mapping>

          屬性配置文件resource.properties中

          TOKEN_KEY=token

          功能實(shí)現(xiàn) 
          服務(wù)層:e3-sso-service中

          1. 接收參數(shù),判斷用戶名和密碼正確

          2. 用戶名和密碼都正確的話,生成token,相當(dāng)于tomcat時(shí)候的jsessionId.用uuid生成,保證唯一性。

          3. 將用戶信息存入redis。Key為”SESSION”+token,value為查詢的用戶信息轉(zhuǎn)為的json串。

          4. 設(shè)置key的有效期。一般為半個(gè)小時(shí)

          5. 返回包裝了token的E3Result

          /*
           * 用戶登錄處理
           */
          @Service
          public class LoginServiceImpl implements LoginService{

              @Autowired
              private TbUserMapper userMapper;
              @Autowired
              private JedisClient jedisClient;
              @Value("${SESSION_EXPIRE}")
              private Integer SESSION_EXPIRE;
              public E3Result userLogin(String username, String password) {

                  //1.判斷用戶名和密碼是否正確
                  //根據(jù)用戶名查詢用戶信息
                  TbUserExample example = new TbUserExample();
                  Criteria criteria = example.createCriteria();
                  criteria.andUsernameEqualTo(username);
                  List<TbUser> list = userMapper.selectByExample(example);
                  if(list == null || list.size() == 0){
                      //返回登錄失敗
                      return E3Result.build(400, "用戶名或者密碼錯(cuò)誤");
                  }
                  //取用戶信息
                  TbUser user = list.get(0);
                  //判斷密碼是否正確
                  if(!DigestUtils.md5DigestAsHex(password.getBytes()).equals(user.getPassword())){
                      //2.如果不正確返回登錄失敗
                      return E3Result.build(400, "用戶名或者密碼錯(cuò)誤");
                  }
                  //3.如果正確生成token
                  String token = UUID.randomUUID().toString();//生成的uuid必不重復(fù)
                  //4.把用戶信息寫(xiě)入redis,key:token,value:用戶信息
                  user.setPassword(null);
                  jedisClient.set("SESSION:"+ token, JsonUtils.objectToJson(user));
                  //5.設(shè)置Session的過(guò)期時(shí)間
                  jedisClient.expire("SESSION:"+ token, SESSION_EXPIRE);
                  //6.把token返回
                  return E3Result.ok(token);
              }
          }

          注:如果沒(méi)用過(guò)mybatis逆向工程那么花2小時(shí)學(xué)一下這樣看上面的代碼更清晰。

          E3Result為自定義響應(yīng)類,如下

          public class E3Result implements Serializable{

              // 定義jackson對(duì)象
              private static final ObjectMapper MAPPER = new ObjectMapper();
              // 響應(yīng)業(yè)務(wù)狀態(tài)
              private Integer status;
              // 響應(yīng)消息
              private String msg;
              // 響應(yīng)中的數(shù)據(jù)
              private Object data;
              public static E3Result build(Integer status, String msg, Object data) {
                  return new E3Result(status, msg, data);
              }
              public static E3Result ok(Object data) {
                  return new E3Result(data);
              }
              public static E3Result ok() {
                  return new E3Result(null);
              }
              public E3Result() {
              }
              public static E3Result build(Integer status, String msg) {
                  return new E3Result(status, msg, null);
              }
              public E3Result(Integer status, String msg, Object data) {
                  this.status = status;
                  this.msg = msg;
                  this.data = data;
              }
              public E3Result(Object data) {
                  this.status = 200;
                  this.msg = "OK";
                  this.data = data;
              }
              get、set方法
             }

          服務(wù)寫(xiě)完之后需要發(fā)布服務(wù)(使用了Dubbo發(fā)布服務(wù),Zookeeper作為注冊(cè)中心) 
          配置文件4中:組件掃描,發(fā)布服務(wù)

          <context:component-scan base-package="cn.e3mall.sso.service"/>

              <!-- 使用dubbo發(fā)布服務(wù) -->
              <!-- 提供方應(yīng)用信息,用于計(jì)算依賴關(guān)系 -->
              <dubbo:application name="e3-sso" />
              <dubbo:registry protocol="zookeeper"
                  address="192.168.25.128:2181" />
              <!-- 用dubbo協(xié)議在20880端口暴露服務(wù) -->
              <dubbo:protocol name="dubbo" port="20883" /><!-- 一個(gè)服務(wù)對(duì)應(yīng)一個(gè)端口 -->
              <!-- 聲明需要暴露的服務(wù)接口 -->
              <dubbo:service interface="cn.e3mall.sso.service.LoginService" 
                  ref="loginServiceImpl" timeout="600000"/>
              <dubbo:service>

          然后將e3-sso工程安裝到本地倉(cāng)庫(kù)

          表現(xiàn)層:e3-sso-web中 
          先要接收服務(wù),spring配置文件中

          <!-- 加載配置文件 -->
              <context:property-placeholder location="classpath:conf/resource.properties" />

              <context:component-scan base-package="cn.e3mall.sso.controller" />
              <mvc:annotation-driven />
              <bean
                  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                  <property name="prefix" value="/WEB-INF/jsp/" />
                  <property name="suffix" value=".jsp" />
              </bean>
              <mvc:resources location="/css/" mapping="/css/**"/>
              <mvc:resources location="/js/" mapping="/js/**"/>
              <mvc:resources location="/images/" mapping="/images/**"/>

              <!-- 引用dubbo服務(wù) -->
              <dubbo:application name="e3-sso-web"/>
              <dubbo:registry protocol="zookeeper" address="192.168.25.128:2181"/>    
              <dubbo:reference interface="cn.e3mall.sso.service.LoginService" id="loginService" />

          處理流程: 
          1. 傳入用戶名和密碼,調(diào)用service層。獲取E3Result。 
          2. 根據(jù)status判斷登錄是否成功 
          3. 從獲得的E3Result中取token信息,存入cookie 
          4. 返回結(jié)果給頁(yè)面

          /*
           * 用戶登錄處理
           */
          @Controller
          public class LoginController {

              @Autowired
              private LoginService loginService;
              @Value("${TOKEN_KEY}")
              private String TOKEN_KEY;//TOKEN_KEY=token
              @RequestMapping("/page/login")
              public String toLogin(String redirect,Model model){
                  model.addAttribute("redirect", redirect);
                  //返回登錄頁(yè)面
                  return "login";
              }

              @RequestMapping(value="/user/login",method=RequestMethod.POST)
              @ResponseBody
              public E3Result login(String username,String password,
                      HttpServletRequest request,HttpServletResponse response){
                  E3Result result = loginService.userLogin(username, password);
                  //判斷是否登錄成功
                  if(result.getStatus()==200){
                      String token = (String) result.getData();
                      //如果登錄成功,token寫(xiě)入cookie 
                      CookieUtils.setCookie(request, response, TOKEN_KEY, token);
                  }
                  return result;
              }
          }

          注:cookie一般默認(rèn)是不能跨域的(商城采用了分布式架構(gòu),所以每個(gè)模塊對(duì)應(yīng)的域名肯定是不一樣的,將比如上面的e3-sso端口號(hào)是8089,e3-sso-web是8090),但是這里寫(xiě)的Cookie工具類CookieUtils的setCookie()方法中對(duì)跨域是進(jìn)行了處理的。

          private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
                      String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
                  try {
                      if (cookieValue == null) {
                          cookieValue = "";
                      } else if (isEncode) {
                          cookieValue = URLEncoder.encode(cookieValue, "utf-8");
                      }
                      Cookie cookie = new Cookie(cookieName, cookieValue);
                      if (cookieMaxage > 0)
                          cookie.setMaxAge(cookieMaxage);
                      if (null != request) {// 設(shè)置域名的cookie
                          String domainName = getDomainName(request);
                          System.out.println(domainName);
                          if (!"localhost".equals(domainName)) {
                              cookie.setDomain(domainName);
                          }
                      }
                      cookie.setPath("/");
                      response.addCookie(cookie);
                  } catch (Exception e) {
                       e.printStackTrace();
                  }
              }

          到這里工程就算做完了。 
          當(dāng)用戶登錄的時(shí)候,先進(jìn)行校驗(yàn),校驗(yàn)通過(guò)后,生成token,作為key,查出來(lái)的用戶信息作為value存入到redis中并設(shè)置key的過(guò)期時(shí)間。并且將token返回給表現(xiàn)層,表現(xiàn)層將token存入Cookie中。當(dāng)用戶訪問(wèn)其它模塊,比如訂單模塊的時(shí)候,我們可以寫(xiě)個(gè)攔截器,攔截請(qǐng)求,判斷用戶是否登錄,從Cookie中取token,如果沒(méi)取到token說(shuō)明用戶根本沒(méi)登錄所以跳轉(zhuǎn)到登錄頁(yè)面,如果取到了token,那么根據(jù)token去redis中查詢用戶信息,說(shuō)明key已經(jīng)失效了,跳轉(zhuǎn)到登錄頁(yè)面。否則放行。

          比如訪問(wèn)購(gòu)物車(chē)系統(tǒng)(天貓?jiān)L問(wèn)購(gòu)物車(chē)要求是登錄狀態(tài)下): 
          在e3-order-web工程中編寫(xiě)攔截器

          /*
           * 用戶登錄處理
           */
          public class LoginInterceptor implements HandlerInterceptor {

              @Autowired
              private TokenService tokenService;

              public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                      throws Exception {
                  //前處理,執(zhí)行handler之前執(zhí)行此方法
                  //返回true:放行  false:攔截
                  //1.從cookie中取token
                  String token = CookieUtils.getCookieValue(request, "token");
                  //2.如果沒(méi)有token,未登錄狀態(tài)
                  if(StringUtils.isBlank(token)){
                      return true;
                  }
                  //3.如果取到token,需要調(diào)用sso系統(tǒng)的服務(wù),根據(jù)token取用戶信息
                  E3Result e3Result = tokenService.getUserByToken(token);
                  if (e3Result.getStatus()!=200){
                      //4.沒(méi)有取到用戶信息,登錄已經(jīng)過(guò)期,直接放行
                      return true;
                  }
                  //5.取到用戶信息。登錄狀態(tài)。
                  TbUser user = (TbUser) e3Result.getData();
                  //6.把用戶信息放到request中,只需要在controller中判斷request中是否包含user信息。
                  request.setAttribute("user", user);
                  return true;
              }

              public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                      ModelAndView modelAndView) throws Exception {
                  //handler執(zhí)行之后,返回modelAndView之前
              }

              public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                      throws Exception {
                  //完成處理,返回modelAndView之后(已經(jīng)響應(yīng)了)
                  //可以再次處理異常
              }

          }

          注:這里需要在sso系統(tǒng)中發(fā)布一個(gè)新的服務(wù)

          <dubbo:service interface="cn.e3mall.sso.service.TokenService" 
                  ref="tokenServiceImpl" timeout="600000"/>

          該服務(wù)是根據(jù)token取用戶信息

          /*
           * 根據(jù)token取用戶信息
           */
          @Service
          public class TokenServiceImpl implements TokenService{

              @Autowired
              private JedisClient jedisClient;
              @Value("${SESSION_EXPIRE}")
              private Integer SESSION_EXPIRE;

              public E3Result getUserByToken(String token) {
                  //根據(jù)token到redis中取用戶信息
                  String json = jedisClient.get("SESSION:"+ token);
                  if(StringUtils.isBlank(json)){
                      //取不到信息,登錄過(guò)期,返回登錄過(guò)期
                      return E3Result.build(201, "用戶登錄已經(jīng)過(guò)期");
                  }
                  //取到用戶信息,跟新token的過(guò)期時(shí)間
                  jedisClient.expire("SESSION:"+ token, SESSION_EXPIRE);
                  //返回結(jié)果,E3Result其中包含用戶對(duì)象
                  TbUser user = JsonUtils.jsonToPojo(json, TbUser.class);
                  return E3Result.ok(user); 
              }
          }

          然后在e3-cart-web工程中引用該服務(wù)

              <dubbo:reference interface="cn.e3mall.sso.service.TokenService" id="tokenService" />

          測(cè)試: 
           
          點(diǎn)擊登錄。提示登錄成功后會(huì)跳轉(zhuǎn)頁(yè)面到首頁(yè).
          查看redis 
           





          版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接和本聲明。

          本文鏈接:

          https://blog.csdn.net/qq_37334135/article/details/77727456






          粉絲福利:Java從入門(mén)到入土學(xué)習(xí)路線圖

          ??????

          ??長(zhǎng)按上方微信二維碼 2 秒


          感謝點(diǎn)贊支持下哈 

          瀏覽 49
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天撸一撸免费视频 | 91逼逼| 西方18毛片视频在线免费观看 | 成人自拍视频 | 狠狠撸天天撸日日撸无码 |