SSO單點登陸

單點登陸系統介紹

單點登陸(Single Sign On),簡稱爲 SSO,是目前比較流行的企業業務整合的解決方案之一。SSO 的定義是在多個應用系統中,用戶只須要登陸一次就能夠訪問全部相互信任的應用系統。css

 

 

 

單點登陸的實現原理

實現單點登陸,就是解決如何產生和存儲那個信任,再就是其餘系統如何驗證這個信任的有效性。所以,也就須要解決如下兩點:html

  • 存儲信任
  • 驗證信任

 

 

項目配置文件(SSM+Redis+Dubbo)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://code.alibabatech.com/schema/dubbo
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- 添加服務消費者的標誌 -->
    <dubbo:application name="ego-sso-web-consumer"/>

    <!-- 指定註冊中心,有兩個地址192.168.1.17一、172.18.25.171 -->
    <dubbo:registry address="172.18.25.171:2181,172.18.25.171:2182,172.18.25.171:2183" protocol="zookeeper" />
    <!--<dubbo:registry address="192.168.1.171:2181,192.168.1.171:2182,192.168.1.171:2183" protocol="zookeeper" />-->

    <!-- spring容器中存在一個遠程服務的代理對象 -->
    <dubbo:reference interface="com.soldier.ego.rpc.service.UserService" id="userServiceProxy"></dubbo:reference>
</beans>
applicationContext-dubbo.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 加載cache.properties-->
    <!--<context:property-placeholder location="classpath:cache.properties"></context:property-placeholder>-->

    <!-- 實例化JedisCluster,鏈接redis集羣,有兩個地址192.168.1.17四、172.18.25.174-->
    <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
        <constructor-arg name="nodes">
            <set>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="172.18.25.174"></constructor-arg>
                    <constructor-arg name="port" value="6380"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="172.18.25.174"></constructor-arg>
                    <constructor-arg name="port" value="6381"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="172.18.25.174"></constructor-arg>
                    <constructor-arg name="port" value="6382"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="172.18.25.174"></constructor-arg>
                    <constructor-arg name="port" value="6383"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="172.18.25.174"></constructor-arg>
                    <constructor-arg name="port" value="6384"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg name="host" value="172.18.25.174"></constructor-arg>
                    <constructor-arg name="port" value="6385"></constructor-arg>
                </bean>
            </set>
        </constructor-arg>
    </bean>
</beans>
applicationContext-redis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                  http://www.springframework.org/schema/beans/spring-beans.xsd
                  http://www.springframework.org/schema/context
                  http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.soldier.ego.sso.service.impl" />
</beans>
applicationContext-service.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 掃描controller -->
    <context:component-scan base-package="com.soldier.ego.sso.controller" />

    <!-- mvc:annotation-driven -->
    <mvc:annotation-driven />

    <!-- 靜態資源映射 -->
    <!-- location:表示資源在項目的真正位置 -->
    <!-- mapping:訪問路徑 -->
    <!-- /css/** -->
    <!-- http://localhost:8080/css/a/b/c/hello.css -->
    <!-- / = http://localhost:8080/ -->
    <mvc:resources location="/css/" mapping="/css/**"></mvc:resources>
    <mvc:resources location="/js/" mapping="/js/**"></mvc:resources>
    <mvc:resources location="/images/" mapping="/images/**"></mvc:resources>

    <!-- 視圖解析器 -->
    <bean id="viewResovler"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 表示使用的視圖技術是jsp -->
        <property name="viewClass"
                  value="org.springframework.web.servlet.view.JstlView"></property>
        <!-- 前綴 -->
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <!-- 後綴 -->
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>
springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
  <display-name>ego-sso-web</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>

  <!--<servlet-mapping>-->
  <!--<servlet-name>default</servlet-name>-->
  <!--<url-pattern>/favicon.ico</url-pattern>-->
  <!--</servlet-mapping>-->

  <!-- 以監聽器的方式啓動spring容器 -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!-- 指定spring的配置文件 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/applicationContext-*.xml</param-value>
  </context-param>

  <!-- POST請求的亂碼過濾器 -->
  <filter>
    <filter-name>encodingFilter</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 -->
  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <!-- springmvc的servlet -->
  <servlet>
    <servlet-name>ego-sso-web</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 指定springmvc的配置文件 -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring/springmvc.xml</param-value>
    </init-param>
    <!-- 讓springmvc隨系統啓動而啓動 -->
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>ego-sso-web</servlet-name>
    <!-- 不須要僞靜態化-->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>
web.xml

 

 

實現用戶名惟一性驗證

一、惟一性驗證接口開發規範

請求方法 GET
UR http://sso.egou.com/user/check/{param}/{type}
參數說 格式如:zhangsan/1,其中 zhangsan 是校驗的數據,type 爲類型,可
選參數 一、二、3 分別表明 username、phone、email
可選參數 callback:若是有此參數表示此方法爲 jsonp 請求,須要支 持 jsonp。
示例 http://sso.egou.com/user/check/zhangsan/1
返回 {
status: 200 //200 成功
msg: "OK"// 返回信息消息
data: false // 返回數據,true:數據可用,false:數據不可用
}

二、實現

  /**
     * 處理用戶名惟一性驗證請求
     * @param param     驗證數據
     * @param type      驗證類型
     * @param callback  回到函數
     *      MappingJacksonValue --> 返回json,支持JSONP(實際上是解決JS跨域調用數據的一種方案)
     *      required = false --> 非必須
     *      @ResponseBody 異步的,不會進行跳轉
     */
    @RequestMapping("/user/check/{param}/{type}")
    @ResponseBody
    public MappingJacksonValue loadPage(@PathVariable String param, @PathVariable Integer type,
                                        @RequestParam(required = false) String callback) {
        EgoResult result = ssoUserService.selectUserByCond(param, type);

        //  處理json響應數據格式
        MappingJacksonValue jacksonValue = new MappingJacksonValue(result);
        if (!StringUtils.isEmpty(callback)) jacksonValue.setJsonpFunction(callback);

        return jacksonValue;
    }
@Service
public class SsoUserServiceImpl implements SsoUserService {

    //注入的是遠程服務的代理對象
    @Autowired
    private UserService userServiceProxy;

    @Override
    public EgoResult selectUserByCond(String cond, Integer type) {
        return userServiceProxy.selectUserByCondService(cond, type);
    }
}
SsoUserService.class
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private TbUserMapper tbUserMapper;

    @Override
    public EgoResult selectUserByCondService(String cond, Integer type) {

        //動態產生where條件
        TbUserExample example = new TbUserExample();
        TbUserExample.Criteria criteria = example.createCriteria();

        //封裝查詢條件
        if (type.equals(1)) {
            criteria.andUsernameEqualTo(cond);
        } else if (type.equals(2)) {
            criteria.andPhoneEqualTo(cond);
        } else if (type.equals(3)) {
            criteria.andEmailEqualTo(cond);
        }

        List<TbUser> userList = tbUserMapper.selectByExample(example);

        //  建立EgoResult對象
        EgoResult result = new EgoResult();
        result.setStatus(200);
        result.setMsg("ok");
        if (userList!=null && userList.size()>0) result.setData(false);
        else result.setData(true);  //  用戶名可用

        return result;
    }
}
UserServiceImpl.class

 

實現用戶註冊

一、用戶註冊接口開發規範

請求方法 POST
URL http://sso.egou.com/user/register
參數說明 username //用戶名
password //密碼
phone //手機號
email //郵箱
示例 http://sso.egou.com/user/register
返回值 {
status: 400
msg: "註冊失敗. 請校驗數據後請再提交數據."
data: null
}

 

二、實現

    /**
     * 實現用戶註冊
     * @param user  用戶信息
     *      @ResponseBody 異步的,不會進行跳轉
     */
    @RequestMapping(value = "/user/register", method = RequestMethod.POST)
    @ResponseBody
    public EgoResult insertUser(TbUser user) {
        return ssoUserService.insertUserService(user);
    }
@Service
public class SsoUserServiceImpl implements SsoUserService {

    //注入的是遠程服務的代理對象
    @Autowired
    private UserService userServiceProxy;

    @Override
    public EgoResult insertUserService(TbUser user) {
        //  md5加密
        String pwd = user.getPassword();
        String md5 = DigestUtils.md5DigestAsHex(pwd.getBytes());
        user.setPassword(md5);
        return userServiceProxy.insertUserService(user);
    }
}
SsoUserServiceImpl.class
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private TbUserMapper tbUserMapper;

    @Override
    public EgoResult insertUserService(TbUser user) {

        EgoResult result = new EgoResult();

        try {
            Date date = new Date();
            user.setCreated(date);
            user.setUpdated(date);
            tbUserMapper.insert(user);
            result.setStatus(200);
            result.setMsg("註冊成功.");
        } catch (Exception e) {
            result.setStatus(400);
            result.setMsg("註冊失敗. 請校驗數據後請再提交數據.");
            e.printStackTrace();
        }

        return result;
    }
}
UserServiceImpl.class

 

實現用戶登陸

一、用戶登陸接口開發規範

請求方式 POST
URL htt://sso.eou.com/user/l
參數說明 username //用戶名
password //密碼
示例 http://sso.egou.com/user/loginusername=zhangsan&password=123
返回值 {
status: 200
msg: "OK"
data: "fe5cb546aeb3ce1bf37abcb08a40493e"//登陸成功,返
回 token
}

 

 二、實現

    /**
     * 實現用戶登陸
     * @param username  用戶名
     * @param password  密碼
     *      @ResponseBody 異步的,不會進行跳轉
     */
    @RequestMapping(value = "/user/login", method = RequestMethod.POST)
    @ResponseBody
    public EgoResult userLogin(String username, String password) {
        return ssoUserService.userLogin(username, password);
    }
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private TbUserMapper tbUserMapper;

    @Override
    public TbUser selectUserByUserNameService(String username) {

        //動態產生where條件
        TbUserExample example = new TbUserExample();
        TbUserExample.Criteria criteria = example.createCriteria();

        //封裝查詢條件
        criteria.andUsernameEqualTo(username);

        // where username=?
        List<TbUser> userList = tbUserMapper.selectByExample(example);

        //  由於用戶名惟一
        if (userList!=null && userList.size()==1) return userList.get(0);

        return null;
    }
}
UserServiceImpl.class
@Service
public class SsoUserServiceImpl implements SsoUserService {

    //注入的是遠程服務的代理對象
    @Autowired
    private UserService userServiceProxy;

    // 注入JedisCluster集羣訪問對象
    @Autowired
    private JedisCluster jedisCluster;

    @Override
    public EgoResult userLogin(String username, String password) {

        EgoResult result = new EgoResult();
        result.setStatus(400);
        result.setData(null);
        result.setMsg("用戶名或密碼錯誤.");
        TbUser tbUser = userServiceProxy.selectUserByUserNameService(username);

        if (tbUser != null) {
            //對前端提交的密碼進行加密
            password = DigestUtils.md5DigestAsHex(password.getBytes());
            if (password.equals(tbUser.getPassword())) {
                //  將當前登陸用戶對象,轉爲json字符串,保存到redis數據庫
                String userJson = JsonUtils.objectToJson(tbUser);
                String token = UUID.randomUUID().toString();
                // 將用戶信息保存到redis數據庫
                jedisCluster.set(token, userJson);

                result.setStatus(200);
                result.setMsg("登陸成功.");
                result.setData(token);
            }
        }

        return result;
    }
}
SsoUserServiceImpl.class
相關文章
相關標籤/搜索