Jeeplus-ani框架单点登录 解决方案
蓝色忧郁
VIP5
2019-03-08 16:29:56
最后修改:2019-03-08 16:32:52
收藏
Jeeplus-ani框架单点登录(感谢guotao同学提供分享)
1 解决方案
概述
单点登录英文全称Single Sign On,简称就是SSO。它的解释是:在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。
2.1 技术实现
登录时序图
img[/userfiles/fly/c6e420a6461044258fd69a3b82ca7c34/files/1552033523034.]
1) 用户登录A系统。
2) 用户在A系统中点击访问“第三方系统”。
3) A根据规则,生成用于第三方系统登录验证的Code。
4) A调用第三方系统登录URL,将Code传给第三方系统。
5) B系统进行验证,验证通过,重定向到第三方系统首页;验证不通过,B自身提示页面。
2.2 实现原理
创建web请求方法,可按照实际业务需求自定义规则实现
img[/userfiles/fly/c6e420a6461044258fd69a3b82ca7c34/files/1552033612107.]
创建令牌类,增加免密属性
img[/userfiles/fly/c6e420a6461044258fd69a3b82ca7c34/files/1552033647235.]
自定义重写免密匹配策略
img[/userfiles/fly/c6e420a6461044258fd69a3b82ca7c34/files/1552033668258.]
配置文件定义匹配策略Bean
img[/userfiles/fly/c6e420a6461044258fd69a3b82ca7c34/files/1552033686805.]
[pre]
/**
* 登录Controller
* @author jeeplus
* @version 2016-5-31
*/
@Api(value = "LoginController", description = "登录控制器")
@Controller
public class LoginController extends BaseController{
@Autowired
private SessionDAO sessionDAO;
@Autowired
private OaNotifyService oaNotifyService;
@Autowired
private MailBoxService mailBoxService;
private SystemService systemService;
@Value("${limit}")
private String limit;
@Value("${app_id}")
private String app_id;
@Value("${minutes}")
private Long minutes;
@Value("${aesKey}")
private String aesKey;
/**
* 单点登录(如已经登录,则直接跳转)
* @param code
* 1)获得code。
* 2)调用SecurityUtils.decryptAES(code),对code进行解密。
* 3)提取解密后字符串的三部分:用户名、时间戳和app_id。
* 4)验证app_id是否正确。
* 5)验证时间戳,和服务器时间比较,前后相差不能多于5分钟,否则提示无权限。
* 6)根据用户名,到第三方系统用户映射表中(参考检测系统用户映射表)找到该用户对应的第三方系统用户名。
* 7)根据用户名到第三方系统中找到用户信息,并进行内部登录。参考这个文件中的逻辑(点右键,下载)。这里就是模拟用户在浏览器发送登录请求,只不过把请求参数在这里生成好后,直接调用login.action进行登录。如果登录成功,系统会自动跳转到首页。
*/
@RequestMapping(value = "${adminPath}/sso")
public String sso(String code,Model model) {
Principal principal = UserUtils.getPrincipal();
// 如果已经登录 返回首页
if(principal != null){
return "modules/sys/login/sysIndex";
}
// 进行单点登录
if (StringUtils.isNotBlank(code)){
//对code进行指定key解密
String key = Cryptos.byte2hex(aesKey.getBytes());
String decryptCode = Cryptos.aesDecrypt(code, key);
String userName;
String timestamp;
String appId;
List<User> users;
try {
String[] codes = decryptCode.split("-");
userName = codes[0];
timestamp = codes[1];
appId = codes[2];
//姓名校验 重名则返回登陆页
User user = new User();
user.setName(userName);
users = systemService.findUser(user);
//若根据姓名查出size不等于 重名或者无用户则返回登录页
if(users.size()!=1){
model.addAttribute(FormAuthenticationFilter.DEFAULT_MESSAGE_PARAM, "用户名密码错误,请检查。");
return "modules/sys/login/sysLogin";
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm");
long time = sdf.parse(timestamp).getTime();
//appid校验
if(!app_id.equals(appId)){
model.addAttribute(FormAuthenticationFilter.DEFAULT_MESSAGE_PARAM, "appId验证失败,请检查。");
return "modules/sys/login/sysLogin";
}
//时间戳校验 前后相差不能多于5分钟
Long c = System.currentTimeMillis() - time;
Long m = c/1000/60;
if(!(minutes>=m && -minutes<=m)){
model.addAttribute(FormAuthenticationFilter.DEFAULT_MESSAGE_PARAM, "时间戳验证失败,前后相差不能多于5分钟,请检查。");
return "modules/sys/login/sysLogin";
}
}catch (Exception e){
model.addAttribute(FormAuthenticationFilter.DEFAULT_MESSAGE_PARAM, "数据传输有误,请检查。");
return "modules/sys/login/sysLogin";
}
try {
UsernamePasswordToken upt = new UsernamePasswordToken(users.get(0).getLoginName());
UserUtils.getSubject().login(upt);
return "modules/sys/login/sysIndex";
} catch (Exception ae) {
model.addAttribute(FormAuthenticationFilter.DEFAULT_MESSAGE_PARAM, "授权错误,请检查用户配置,若不能解决,请联系管理员。");
}
}else{
model.addAttribute(FormAuthenticationFilter.DEFAULT_MESSAGE_PARAM, "Code不能为空,请检查。");
}
return "modules/sys/login/sysLogin";
}
public SystemService getSystemService() {
if (systemService == null){
systemService = SpringContextHolder.getBean(SystemService.class);
}
return systemService;
}
/**
* 管理登录
* @throws IOException
*/
@ApiOperation(notes = "login", httpMethod = "POST", value = "用户登录")
@ApiImplicitParams({@ApiImplicitParam(name = "username", value = "用户名", required = true, paramType = "query",dataType = "string"),
@ApiImplicitParam(name = "password", value = "密码", required = true, paramType = "query",dataType = "string"),
@ApiImplicitParam(name="mobileLogin",value = "接口标志",required = true, paramType = "query",dataType = "string")})
@RequestMapping(value = "${adminPath}/login")
public String login(HttpServletRequest request, HttpServletResponse response) throws IOException {
Principal principal = UserUtils.getPrincipal();
if (logger.isDebugEnabled()){
logger.debug("login, active session size: {}", sessionDAO.getActiveSessions(false).size());
}
// 如果已登录,再次访问主页,则退出原账号。
if (Global.TRUE.equals(Global.getConfig("notAllowRefreshIndex"))){
CookieUtils.setCookie(response, "LOGINED", "false");
}
// 如果已经登录,则跳转到管理首页
if(principal != null && !principal.isMobileLogin()){
return "redirect:" + adminPath;
}
SavedRequest savedRequest = WebUtils.getSavedRequest(request);//获取跳转到login之前的URL
// 如果是手机没有登录跳转到到login,则返回JSON字符串
if(savedRequest != null){
String queryStr = savedRequest.getQueryString();
if( queryStr!=null &&( queryStr.contains("__ajax") || queryStr.contains("mobileLogin"))){
AjaxJson j = new AjaxJson();
j.setSuccess(false);
j.setErrorCode("0");
j.setMsg("没有登录!");
return renderString(response, j);
}
}
return "modules/sys/login/sysLogin";
}
/**
* 登录失败,真正登录的POST请求由Filter完成
*/
@RequestMapping(value = "${adminPath}/login", method = RequestMethod.POST)
public String loginFail(HttpServletRequest request, HttpServletResponse response, Model model) {
Principal principal = UserUtils.getPrincipal();
// 如果已经登录,则跳转到管理首页
if(principal != null){
return "redirect:" + adminPath;
}
String username = WebUtils.getCleanParam(request, FormAuthenticationFilter.DEFAULT_USERNAME_PARAM);
boolean rememberMe = WebUtils.isTrue(request, FormAuthenticationFilter.DEFAULT_REMEMBER_ME_PARAM);
boolean mobile = WebUtils.isTrue(request, FormAuthenticationFilter.DEFAULT_MOBILE_PARAM);
String exception = (String)request.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);
String message = (String)request.getAttribute(FormAuthenticationFilter.DEFAULT_MESSAGE_PARAM);
if (StringUtils.isBlank(message) || StringUtils.equals(message, "null")){
message = "用户或密码错误, 请重试.";
}
model.addAttribute(FormAuthenticationFilter.DEFAULT_USERNAME_PARAM, username);
model.addAttribute(FormAuthenticationFilter.DEFAULT_REMEMBER_ME_PARAM, rememberMe);
model.addAttribute(FormAuthenticationFilter.DEFAULT_MOBILE_PARAM, mobile);
model.addAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME, exception);
model.addAttribute(FormAuthenticationFilter.DEFAULT_MESSAGE_PARAM, message);
if (logger.isDebugEnabled()){
logger.debug("login fail, active session size: {}, message: {}, exception: {}",
sessionDAO.getActiveSessions(false).size(), message, exception);
}
// 非授权异常,登录失败,验证码加1。
if (!UnauthorizedException.class.getName().equals(exception)){
model.addAttribute("isValidateCodeLogin", isValidateCodeLogin(username, true, false));
}
// 验证失败清空验证码
request.getSession().setAttribute(ValidateCodeServlet.VALIDATE_CODE, IdGen.uuid());
// 如果是手机登录,则返回JSON字符串
if (mobile){
AjaxJson j = new AjaxJson();
j.setSuccess(false);
j.setMsg(message);
j.put("username", username);
j.put("name","");
j.put("mobileLogin", mobile);
j.put("JSESSIONID", "");
return renderString(response, j.getJsonStr());
}
return "modules/sys/login/sysLogin";
}
/**
* 管理登录
* @throws IOException
*/
@RequestMapping(value = "${adminPath}/logout", method = RequestMethod.GET)
public String logout(HttpServletRequest request, HttpServletResponse response, Model model) throws IOException {
Principal principal = UserUtils.getPrincipal();
// 如果已经登录,则跳转到管理首页
if(principal != null){
UserUtils.getSubject().logout();
}
// 如果是手机客户端退出跳转到login,则返回JSON字符串
String ajax = request.getParameter("__ajax");
if( ajax!=null){
model.addAttribute("success", "1");
model.addAttribute("msg", "退出成功");
return renderString(response, model);
}
return "redirect:" + adminPath+"/login";
}
[/pre]