来源:https://www.itdaan.com/blog/2016/12/27/f5f37de997975cf2edcfd7f955dab754.html
1. 引言
之前介绍了kisso与jfinal的简单使用,但是单单这种方式满足不了一些需求,举个例子,一个系统的中分别有很多的子系统,这样的情况下我们做单点登陆,就面临着一个问题,在不同的子系统中IP地址不一样,域名不一样,相对的用户登陆自后所对应的cookie就不能带到其他系统上去。相当于之前登陆的是无效的,又要重新进行登陆。这就所谓的跨域问题。
2. 流程
kisso解决跨域的流程:
大致意思是,用户登陆,访问sso服务器,进行账户密码校验,如果登陆成功则返回一个token给用户返回的形式为cookie。然后 用户又去访问其他的子系统,这是由于域名不一样,先前的登陆成功的token就不会被带过去,子系统发现用户没有token,就跳转sso系统询问用户是否登陆,这时跳转就会带上我们之前的token了,sso系统拿到用户的token就代表着用户登陆过。然后返回一个应答给子系统也就是这里的my了。子系统收到sso的应答信息,判断用户是否登陆过,如果登陆过就重新生成一个token,注意这里的token跟先前的token是不会冲突的,因为domain不一样。最后就返回给用户想访问的页面。
3. 具体过程
先配置sso系统。
1.1 添加依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>kisso</artifactId>
<version>3.6.13</version>
</dependency>
1.2 kisso配置文件
因为kisso的默认的加密机制是RC4,这个加密方式是可以更改的。sso系统必须的保存各个系统的公钥,以及自己的私钥,为什么呢?因为涉及到数据的验证以及数据的加密。这不是废话么,好了,子系统向sso询问用户是否登陆的时候会发送一些加密信息,然后sso系统拿到这些信息后就要用子系统的公钥来验证是否是子系统所发送的,然后在向子系统发送应答信息,当然这也是加密的,子系统拿到以后在进行验证。从而保证了一定的安全性。上面的篇日志文件还有一点需要注意的是sso.authcookie.secretkey这个属性每个系统都要写成一样的,方便加密与解密。
1.3 插件代码如下
/**
* Kisso jfinal 插件形式初始化
* jfinal 拦截器不够灵活,因此写在 demo 中,方便您自己修改。
*/
public class KissoJfinalPlugin implements IPlugin {
protected static final Logger logger = Logger.getLogger("WebKissoConfigurer");
/**
* 初始化
*/
public boolean start() {
WebKissoConfigurer ssoConfig = new WebKissoConfigurer("sso.properties");
// 此处可以实现自己的 KISSO 插件,也可动态注入 SSO 配置属性。
// ssoConfig.setPluginList(pluginList);
//运行模式设置,可选择指定模式的配置
ssoConfig.setRunMode("test_mode");
ssoConfig.initKisso();
return true;
}
/**
* 销毁
*/
public boolean stop() {
logger.info("Uninstalling Kisso ");
return true;
}
}
1.4 jfinal的配置如下
public class DemoConfig extends JFinalConfig {
//配置设置
@Override
public void configConstant(Constants constants) {
constants.setDevMode(true);
constants.setEncoding("utf-8");
}
//配置路由
@Override
public void configRoute(Routes routes) {
routes.add("/", IndexController.class);
routes.add("/login", LoginController.class);
routes.add("/logout", LogoutController.class);
routes.add("/reply", ReplyLoginController.class);
}
@Override
public void configPlugin(Plugins plugins) {
KissoJFinalPlugin kissoJFinalPlugin = new KissoJFinalPlugin();
//初始化插件
plugins.add(kissoJFinalPlugin);
}
@Override
public void configInterceptor(Interceptors interceptors) {
}
@Override
public void configHandler(Handlers handlers) {
}
}
1.5 登录设置
public void index(){
HttpServletRequest request = getRequest();
HttpServletResponse Response = getResponse();
String returnURL = getPara("returnURL");
String referer = getPara("referer");
String str = getPara("str");
String info = getPara("info","");
MyToken mt = SSOHelper.getToken(request);
if(info.contains("异常登录")){
mt=null;
SSOHelper.clearLogin(request, Response);
}
if(mt!=null)
forwardAction("/login"); //如果sso有Cookie,则直接尝试登录
else{
System.err.println("没有 Token");
setAttr("str", str);
setAttr("info", info);
setAttr("returnURL", returnURL);
setAttr("referer", referer);
render("login.html"); //弹出登录页面
}
}
public void login() throws IOException{
HttpServletRequest request = getRequest();
HttpServletResponse Response = getResponse();
String returnURL = getPara("returnURL");
String referer = getPara("referer");
String str = getPara("str");
Integer remember = getParaToInt("remember",0);
String username = getPara("username");
String password = getPara("password");
MyToken mt = SSOHelper.getToken(request);
if(mt!=null){
username = mt.getUsername();
password = mt.getPassword();
System.err.println("从Token登录");
}
User u = User.dao.findByUsername(username);
if(u!=null){
UserService userService = new UserService();
if(!userService.passwdValidate(u, password))
u=null;
}
if(u!=null){ //用户验证
if(mt==null){
if(remember==1)
request.setAttribute(SSOConfig.SSO_COOKIE_MAXAGE, 3600*24*5);//保存5天
else
request.setAttribute(SSOConfig.SSO_COOKIE_MAXAGE, -1);//浏览器关闭就清除
mt = new MyToken();
mt.setUsername(username);
mt.setPassword(password);
SSOHelper.setSSOCookie(request, Response, mt, false);
}
if(StrKit.isBlank(returnURL) || StrKit.isBlank(str)) {
setAttr("username", username);
render("index.html"); //sso系统主页面
} else {
HttpClient http=new HttpClient(Response);
String validate = str + salt + username;
validate = EncryptStr.encodeByMD5(validate);
http.setParameter("validate",validate);
http.setParameter("username",username);
http.setParameter("referer",referer);
http.sendByPost(returnURL); //验证通过,重定向到子系统
System.err.println("sso login登录成功:"+username+" "+returnURL+" rememberPW:"+remember);
renderNull();
}
}else{
setAttr("str", str);
setAttr("returnURL", returnURL);
setAttr("referer", referer);
setAttr("info", "用户名或者密码错误");
render("login.html");
SSOHelper.clearLogin(getRequest(), getResponse());
}
}
子系统接收响应,判断用户是否通过,通过直接登录
public void login() {
String validate = getPara("validate");
String username = getPara("username");
User loginUser = getSessionAttr("user");
if(loginUser != null) {
userLogin(loginUser.getUsername());
} else if(StrKit.isBlank(validate)) {
toSSO("", RETURN_URL, SSO_URL);
} else {
String str = getSessionAttr("str");
if(validate.equals(EncryptStr.encodeByMD5(str + ConfigConsts.SALT + username))){
System.err.println("通过验证,使用用户名称登录");
removeSessionAttr("str");
userLogin(username); //用户登录
}else{
toSSO("异常登录", RETURN_URL, SSO_URL);
}
}
}