SSO單點登入-分散式系統實戰
阿新 • • 發佈:2019-01-22
什麼是單點登入SSO(Single Sign-On)
SSO是一種統一認證和授權機制,指訪問同一伺服器不同應用中的受保護資源的同一使用者,只需要登入一次,即通過一個應用中的安全驗證後,再訪問其他應用中的受保護資源時,不再需要重新登入驗證。單點登入解決了什麼問題
解決了使用者只需要登入一次就可以訪問所有相互信任的應用系統,而不用重複登入。例如CSDN中的論壇應用,部落格應用,下載應用模組。我們只要在CSDN中訪問任何一個子應用後,再訪問其他應用站點的時候就不需要在進行登入。
Sso系統工程搭建
使用者登入
效果演示:
核心程式碼:
登入:
package cn.e3mall.sso.service.impl; import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.util.DigestUtils; import cn.e3mall.common.jedis.JedisClient; import cn.e3mall.common.utils.E3Result; import cn.e3mall.common.utils.JsonUtils; import cn.e3mall.mapper.TbUserMapper; import cn.e3mall.pojo.TbUser; import cn.e3mall.pojo.TbUserExample; import cn.e3mall.pojo.TbUserExample.Criteria; import cn.e3mall.sso.service.LoginService; /** * 使用者登入處理 * <p>Title: LoginServiceImpl</p> * <p>Description: </p> * <p>Company: www.itcast.cn</p> * @version 1.0 */ @Service public class LoginServiceImpl implements LoginService { @Autowired private TbUserMapper userMapper; @Autowired private JedisClient jedisClient; @Value("${SESSION_EXPIRE}") private Integer SESSION_EXPIRE; @Override public E3Result userLogin(String username, String password) { // 1、判斷使用者和密碼是否正確 //根據使用者名稱查詢使用者資訊 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, "使用者名稱或密碼錯誤"); } //取使用者資訊 TbUser user = list.get(0); //判斷密碼是否正確 if (!DigestUtils.md5DigestAsHex(password.getBytes()).equals(user.getPassword())) { // 2、如果不正確,返回登入失敗 return E3Result.build(400, "使用者名稱或密碼錯誤"); } // 3、如果正確生成token。 String token = UUID.randomUUID().toString(); // 4、把使用者資訊寫入redis,key:token value:使用者資訊 user.setPassword(null); jedisClient.set("SESSION:" + token, JsonUtils.objectToJson(user)); // 5、設定Session的過期時間 jedisClient.expire("SESSION:" + token, SESSION_EXPIRE); // 6、把token返回 return E3Result.ok(token); } }
package cn.e3mall.sso.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import cn.e3mall.common.utils.CookieUtils; import cn.e3mall.common.utils.E3Result; import cn.e3mall.sso.service.LoginService; /** * 使用者登入處理 * <p>Title: LoginController</p> * <p>Description: </p> * <p>Company: www.itcast.cn</p> * @version 1.0 */ @Controller public class LoginController { @Autowired private LoginService loginService; @Value("${TOKEN_KEY}") private String TOKEN_KEY; @RequestMapping("/page/login") public String showLogin() { return "login"; } @RequestMapping(value="/user/login", method=RequestMethod.POST) @ResponseBody public E3Result login(String username, String password, HttpServletRequest request, HttpServletResponse response) { E3Result e3Result = loginService.userLogin(username, password); //判斷是否登入成功 if(e3Result.getStatus() == 200) { String token = e3Result.getData().toString(); //如果登入成功需要把token寫入cookie System.out.println("token:"+token); CookieUtils.setCookie(request, response, TOKEN_KEY, token); } //返回結果 return e3Result; } }
package cn.e3mall.sso.service.impl; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import cn.e3mall.common.jedis.JedisClient; import cn.e3mall.common.utils.E3Result; import cn.e3mall.common.utils.JsonUtils; import cn.e3mall.pojo.TbUser; import cn.e3mall.sso.service.TokenService; /** * 根據token取使用者資訊 * <p>Title: TokenServiceImpl</p> * <p>Description: </p> * <p>Company: www.itcast.cn</p> * @version 1.0 */ @Service public class TokenServiceImpl implements TokenService { @Autowired private JedisClient jedisClient; @Value("${SESSION_EXPIRE}") private Integer SESSION_EXPIRE; @Override public E3Result getUserByToken(String token) { //根據token到redis中取使用者資訊 String json = jedisClient.get("SESSION:" + token); //取不到使用者資訊,登入已經過期,返回登入過期 if (StringUtils.isBlank(json)) { return E3Result.build(201, "使用者登入已經過期"); } //取到使用者資訊更新token的過期時間 jedisClient.expire("SESSION:" + token, SESSION_EXPIRE); //返回結果,E3Result其中包含TbUser物件 TbUser user = JsonUtils.jsonToPojo(json, TbUser.class); return E3Result.ok(user); } }
package cn.e3mall.sso.controller;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import cn.e3mall.common.utils.E3Result;
import cn.e3mall.sso.service.TokenService;
/**
* 根據token查詢使用者資訊Controller
* <p>Title: TokenController</p>
* <p>Description: </p>
* <p>Company: www.itcast.cn</p>
* @version 1.0
*/
@Controller
public class TokenController {
@Autowired
private TokenService tokenService;
/*@RequestMapping(value="/user/token/{token}",
produces=MediaType.APPLICATION_JSON_UTF8_VALUE"application/json;charset=utf-8")
@ResponseBody
public String getUserByToken(@PathVariable String token, String callback) {
E3Result result = tokenService.getUserByToken(token);
//響應結果之前,判斷是否為jsonp請求
if (StringUtils.isNotBlank(callback)) {
//把結果封裝成一個js語句響應
return callback + "(" + JsonUtils.objectToJson(result) + ");";
}
return JsonUtils.objectToJson(result);
}*/
@RequestMapping(value="/user/token/{token}")
@ResponseBody
public Object getUserByToken(@PathVariable String token, String callback) {
E3Result result = tokenService.getUserByToken(token);
//響應結果之前,判斷是否為jsonp請求
if (StringUtils.isNotBlank(callback)) {
//把結果封裝成一個js語句響應
MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result);
mappingJacksonValue.setJsonpFunction(callback);
return mappingJacksonValue;
}
return result;
}
}