1. MVC Controller 映射 sso 方法。
/** * 單點登錄(如已經登錄,則直接跳轉) * @param userCode 登錄用戶編碼 * @param token 登錄令牌,令牌組成:sso密鑰+用戶名+日期,進行md5加密,舉例: * String secretKey = Global.getConfig("shiro.sso.secretKey"); * String token = Digests.md5(secretKey + userCode + DateUtils.getDate("yyyyMMdd")); * @param url 登錄成功后跳轉的url地址。 * @param relogin 是否重新登錄,需要重新登錄傳遞true * 例如:http://localhost/project/sso/{token}?url=xxx&relogin=true */ @RequestMapping(value = "sso/{userCode}/{token}") public String sso(@PathVariable String userCode, @PathVariable String token, @RequestParam(required=true) String url, String relogin, Model model) { Principal principal = SecurityUtils.getSubject().getPrincipal(); // 如果已經登錄 if(principal != null){ // 如果設置強制重新登錄,則重新登錄 if (BooleanUtils.toBoolean(relogin)){ SecurityUtils.getSubject().logout(); } // 否則,直接跳轉到目標頁 else{ return "redirect:" + Encodes.urlDecode2(url); } } // 進行單點登錄 if (token != null){ UsernamePasswordToken upt = new UsernamePasswordToken(); try { upt.setUsername(userCode); // 登錄用戶名 upt.setPassword(token.toCharArray()); // 密碼組成:sso密鑰+用戶名+日期,進行md5加密,舉例: Digests.md5(secretKey+username+20150101)) upt.setParams(upt.toString()); // 單點登錄識別參數,see: AuthorizingRealm.assertCredentialsMatch } catch (Exception ex){ if (!ex.getMessage().startsWith("msg:")){ ex = new AuthenticationException("msg:授權令牌錯誤,請聯系管理員。"); } model.addAttribute("exception", ex); } try { SecurityUtils.getSubject().login(upt); return "redirect:" + Encodes.urlDecode2(url); } catch (AuthenticationException ae) { if (!ae.getMessage().startsWith("msg:")){ ae = new AuthenticationException("msg:授權錯誤,請檢查用戶配置,若不能解決,請聯系管理員。"); } model.addAttribute("exception", ae); } } return "error/403"; }
2. 重載org.apache.shiro.realm.AuthorizingRealm類的assertCredentialsMatch方法
/** * 認證密碼匹配調用方法 */ @Override protected void assertCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authcToken; // 若單點登錄,則使用單點登錄授權方法。 if (token.toString().equals(token.getParams())){ // sso密鑰+用戶名+日期,進行md5加密,舉例: Digests.md5(secretKey+username+20150101)) String secretKey = Global.getConfig("shiro.sso.secretKey"); String password = Digests.md5(secretKey + token.getUsername() + DateUtils.getDate("yyyyMMdd")); if (password.equals(String.valueOf(token.getPassword()))){ return; } } super.assertCredentialsMatch(token, info); }
3. 實現Shiro無狀態訪問,如通過傳遞sessionid參數即可實現會話訪問
這里需要自定義Shiro的SessionManager類,方法是繼承org.apache.shiro.web.session.mgt.DefaultWebSessionManager類,重載getSessionId方法,如下:
public class SessionManager extends DefaultWebSessionManager { public SessionManager() { super(); } @Override protected Serializable getSessionId(ServletRequest request, ServletResponse response) { // 如果參數中包含“__sid”參數,則使用此sid會話。 例如:http://localhost/project?__sid=xxx&__cookie=true // 其實這里還可以使用如下參數:cookie中的session名稱:如:JSESSIONID=xxx,路徑中的 ;JESSIONID=xxx,但建議還是使用 __sid參數。 String sid = request.getParameter("__sid"); if (StringUtils.isNotBlank(sid)) { // 是否將sid保存到cookie,瀏覽器模式下使用此參數。 if (WebUtils.isTrue(request, "__cookie")){ HttpServletRequest rq = (HttpServletRequest)request; HttpServletResponse rs = (HttpServletResponse)response; Cookie template = getSessionIdCookie(); Cookie cookie = new SimpleCookie(template); cookie.setValue(sid); cookie.saveTo(rq, rs); } // 設置當前session狀態 request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, ShiroHttpServletRequest.URL_SESSION_ID_SOURCE); // session來源與url request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sid); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); return sid; }else{ return super.getSessionId(request, response); } } }