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
相关文章
相关标签/搜索