Jeeplus-ani框架单点登录 解决方案

分享 未结 精帖 1 225
蓝色忧郁
蓝色忧郁 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]
回帖