簡單實現Shiro單點登陸(自定義Token令牌)

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);
        }
    }
 
}
相關文章
相關標籤/搜索