單點登陸多點註銷shiro的簡單實現

方式一 屢次登陸方式

  1. MVC Controller 的 sso 方法
@RequestMapping(value = "ssoLogin.do")
public String sso(String userName, String password,HttpServletRequest request, HttpServletResponse response) {
	AuthUser user = userDao.getUserByname(userName);
	 UsernamePasswordToken token = new UsernamePasswordToken(username, password);
	 //獲取當前的Subject
     Subject currentUser = SecurityUtils.getSubject();
	 currentUser.login(token);
	currentUser.getSession("true").setAttribute("userName",userName);//shrio session
	currentUser.getSession().setAttribute("loginName",userName);//http session
	return "xxx"; //頁面
}
  1. 調用
String path = "osdp";//request.getContextPath();//項目地址
String basePath = request.getScheme() + ":" +  request.getServerName() + ":" +  request.getServerPort() + path;//url
String userName = UserHolder.getUserName();
String password =  UserHolder.getPassword();
String data = "userName="  + userName + "&password="  + password;
return "redirect:" +  basePath + "/ssoLogin" + "?" + data;

方式二 自定義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";
}
  1. 重載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);
}
  1. 實現Shiro無狀態訪問,如經過傳遞sessionid參數便可實現會話訪問
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);
		}
	}
}
  1. 調用
String path = "osdp";//request.getContextPath();//項目地址
String basePath = request.getScheme() + ":" +  request.getServerName() + ":" +  request.getServerPort() + path;//url
String userName = UserHolder.getUserName();
String secretKey = "SSO";
String token =   DigestUtils.md5Hex(secretKey + userName + CommonUtil.getToday());
String data = "userName="  + userName + "&token="  + token;
return "redirect:" +  basePath + "/ssoLogin" + "?" + data;

注意:shiro配置放開單點請求:/sso** =anon

以上兩種方式能夠實現簡單的單點登陸,基本思想就是多個子系統登陸一遍(自定義token登陸或用戶名密碼登陸,原理同樣)。java

多點註銷

每個子項目註銷一遍:apache

final String loginName = session.getAttribute("loginName");//當前登陸賬號

new Thread(new Runnable(){
	@Override
	public void run(){
		HttpUtil.get("xxx/ssoLogout?loginName=" + loginName);//接口調用:註銷其餘項目
	}
}).start();
//註銷
SecurityUtils.getSubject().logout();

void ssoLogout(HttpServletRequest request, HttpServletResponse response){
	String loginName = request.getParameter("loginName"); //註銷賬號
	   
 	// 1.獲取當前用戶sessionId
    String currentUserSessionId = SecurityUtils.getSubject().getSession().getId().toString();
    // 2.獲取shiro的sessionManager
    DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
    DefaultWebSessionManager sessionManager = (DefaultWebSessionManager)securityManager.getSessionManager();
    // 3.獲取全部已登陸用戶的session列表
    Collection<Session> sessions = sessionManager.getSessionDAO().getActiveSessions();
	//清除session
	if(!CollectionUtils.isEmpty(sessions)&&sessions.size() > 1){
		 User user = null;
		for(Session session:sessions){
			 // 4. 獲取已登陸用戶的session的key值
            SimplePrincipalCollection simplePrincipalCollection = (SimplePrincipalCollection) session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            // 5. 獲取new SimpleAuthenticationInfo(user, pwd, this.getName())中放進去的第一個參數
            user = (User) simplePrincipalCollection.getPrimaryPrincipal();
			// 6. 清除當前用戶及之前登陸時保存的session會話
            if (loginName.equals(user.getUserName()) && session.getId().equals(currentUserSessionId)) {
                sessionManager.getSessionDAO().delete(session);
                System.out.println("清理用戶["+loginName+"],SessionId爲["+session.getId()+"]的Session信息!");
            }
		}
	}
}
相關文章
相關標籤/搜索