SSO单点登录
程序员文章站
2023-10-28 23:07:58
单点登录系统介绍 单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。 单点登录的实现原理 实现单点登录,就是解决如何产生和存储那个信任,再就是其他系统如何验证这个 ......
单点登录系统介绍
单点登录(single sign on),简称为 sso,是目前比较流行的企业业务整合的解决方案之一。sso 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
单点登录的实现原理
实现单点登录,就是解决如何产生和存储那个信任,再就是其他系统如何验证这个信任的有效性。因此,也就需要解决以下两点:
- 存储信任
- 验证信任
项目配置文件(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.171、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>
<?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.174、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>
<?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>
<?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>
<?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>
实现用户名唯一性验证
1、唯一性验证接口开发规范
请求方法 | get |
ur | http://sso.egou.com/user/check/{param}/{type} |
参数说 |
格式如:zhangsan/1,其中 zhangsan 是校验的数据,type 为类型,可 选参数 1、2、3 分别代表 username、phone、email 可选参数 callback:如果有此参数表示此方法为 jsonp 请求,需要支 持 jsonp。 |
示例 | http://sso.egou.com/user/check/zhangsan/1 |
返回 |
{ status: 200 //200 成功 msg: "ok"// 返回信息消息 data: false // 返回数据,true:数据可用,false:数据不可用 } |
2、实现
/** * 处理用户名唯一性验证请求 * @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); } }
@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; } }
实现用户注册
1、用户注册接口开发规范
请求方法 | post |
url | http://sso.egou.com/user/register |
参数说明 |
username //用户名 password //密码 phone //手机号 email //邮箱 |
示例 | http://sso.egou.com/user/register |
返回值 |
{ status: 400 msg: "注册失败. 请校验数据后请再提交数据." data: null } |
2、实现
/** * 实现用户注册 * @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); } }
@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; } }
实现用户登录
1、用户登录接口开发规范
请求方式 | 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 } |
2、实现
/** * 实现用户登录 * @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; } }
@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; } }