3.9.單點(diǎn)登錄相關(guān)

2023-07-03 17:08 更新
在BDF2-CORE當(dāng)中,默認(rèn)就提供了對(duì)CASSSO支持,如果我們已經(jīng)有了現(xiàn)成的,并且已在CAS客戶端Server上配置好相關(guān)證書信息,那么就可以通過修改BDF2-CORE模塊中的相關(guān)屬性,快速將BDF2應(yīng)用接入到當(dāng)前的CAS Server當(dāng)中,具體需要修改的屬性如下:
屬性名類型默認(rèn)值描述示例
bdf2.casLoginUrlString/cas.login.d當(dāng)采用CAS進(jìn)行SSO登錄時(shí),設(shè)置CASServer的登錄頁面的URL地址
bdf2.casLoginUrl=https://www.bstek.com:8443/cas-server/login
bdf2.casServerUrlString/cas.server設(shè)置CASServer的URL地址
bdf2.casServerUrl=https://www.bstek.com:8443/cas-server
bdf2.casClientServerUrlStringhttp://localhost:8080/bdf2-test
設(shè)置要采用CASSSO認(rèn)證的客戶端應(yīng)用的地址
bdf2.casClientServerUrl=http://localhost:8080/bdf2-test
bdf2.logoutSuccessURL
String
/bdf2.core.view.response.LogoutSuccess.d
主框架右上角退出系統(tǒng)快捷圖標(biāo)點(diǎn)擊時(shí),退出系統(tǒng)成功后跳轉(zhuǎn)的地址 ,這里設(shè)置為CASSSO的logout,表示在系統(tǒng)內(nèi)部退出完成(銷毀Se
ssion之類操作完成)之后,再跳轉(zhuǎn)到CASSSO的logout進(jìn)行SSO的登出操作。
bdf2.logoutSuccessURL=https://www.bstek.com:8443/cas-server/logout
bdf2.authenticationType
String
form
這個(gè)屬性目標(biāo)支持兩個(gè)值,一個(gè)就是默認(rèn)的form,表示采用BDF2系統(tǒng)提供的登錄表單登錄;另一個(gè)就是cas,表示采用CASSSO登錄。
bdf2.authenticationType=cas
這里需要強(qiáng)調(diào)的是BDF2中對(duì)于CASSSO的支持,我們做了完善的功能測(cè)試,以保證其不會(huì)有問題,所以如果您要使用這一功能,請(qǐng)確保您的CASServer配置正確,確保部署B(yǎng)DF2應(yīng)用的客戶端Server對(duì)于 CAS Server證書配置正確,這樣才能保證能把BDF2中CASSSO支持用起來。一旦出現(xiàn)問題,多數(shù)都是您的環(huán)境配置問題,與BDF2對(duì)CAS SSO支持無關(guān),一句話,您一定要能熟練使用CASServer來構(gòu)建SSO環(huán)境,不能一知半解,邊猜邊做。
如果您需要采用其它的登錄方式,這種登錄既非系統(tǒng)提供的表單登錄,也非CAS的SSO(可能是其它類型的SSO),那么就需要通過下面的兩種方
式實(shí)現(xiàn)。 

第一種方法,就是從BDF2-CORE-1.0.1開始提供的通過實(shí)現(xiàn)IRetrivePreAuthenticatedUser方式實(shí)現(xiàn),該接口的源碼如下所示:
IRetrivePreAuthenticatedUser接口源碼
package com.bstek.bdf2.core.security;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.bstek.bdf2.core.business.IUser;
/**
 * @author Jacky.gao
 * @since 2013年7月5日
 * 獲取通過其它方式已經(jīng)登錄的用戶信息,比如通過SSO等
 */
public interface IRetrivePreAuthenticatedUser {
 /**
 *
根據(jù)給出的request與response對(duì)象,取出當(dāng)前已通過其它途徑預(yù)認(rèn)證的IUser對(duì)象,如果返回null表示預(yù)認(rèn)證未通過
,系統(tǒng)將不會(huì)處理
 * @param request
 * @param response
 * @return 返回已被預(yù)認(rèn)證通過的IUser對(duì)象
 * @throws ServletException
 */
 IUser retrive(HttpServletRequest request,HttpServletResponse response) throws ServletException;
}
可以看到,這個(gè)接口當(dāng)中只有一個(gè)retrive方法,該方法的作用就是要接口實(shí)現(xiàn)類返回當(dāng)前已預(yù)認(rèn)證通過的IUser接口實(shí)現(xiàn)類對(duì)象。該接口編寫完成后需要配置到Spring環(huán)境當(dāng)中,作為一個(gè)標(biāo)準(zhǔn)的SpringBean,系統(tǒng)會(huì)自動(dòng)檢測(cè)到該接口實(shí)現(xiàn)類,這樣用戶未登錄的情況下訪問某個(gè)需要登錄才能訪問的頁面時(shí)會(huì)自動(dòng)調(diào)用這個(gè)接口實(shí)現(xiàn)類,返回已認(rèn)證用戶信息,從而完成用戶自動(dòng)登錄的動(dòng)作。下面的是一個(gè)非常簡(jiǎn)單的IRetrivePreAuthenticatedUser接口實(shí)現(xiàn)類,其源碼如下:
測(cè)試IRetrivePreAuthenticatedUser接口實(shí)現(xiàn)類
package test;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.bstek.bdf2.core.business.IUser;
import com.bstek.bdf2.core.model.DefaultUser;
import com.bstek.bdf2.core.security.IRetrivePreAuthenticatedUser;
import com.bstek.bdf2.core.service.IDeptService;
import com.bstek.bdf2.core.service.IGroupService;
import com.bstek.bdf2.core.service.IPositionService;
@Component
public class TestRetrivePreAuthenticatedUser implements IRetrivePreAuthenticatedUser {
 @Autowired
 @Qualifier(IDeptService.BEAN_ID)
 private IDeptService deptService;
 @Autowired
 @Qualifier(IPositionService.BEAN_ID)
 private IPositionService positionService;
 @Autowired
 @Qualifier(IGroupService.BEAN_ID)
 private IGroupService groupService;
 public IUser retrive(HttpServletRequest request,
 HttpServletResponse response) throws ServletException {
 //從其它源讀取登錄信息,比如某些硬件卡中讀取登錄信息等
 DefaultUser user=new DefaultUser("admin");
 user.setCompanyId("bstek");
 //為登錄成功的用戶設(shè)置所在部門、崗位及群組信息
 user.setDepts(deptService.loadUserDepts(user.getUsername()));
 user.setPositions(positionService.loadUserPositions(user.getUsername()));
 user.setGroups(groupService.loadUserGroups(user.getUsername()));
 //為登錄成功的用戶設(shè)置所在部門、崗位及群組信息結(jié)束
 return user;
 }
}

第二種方法就是實(shí)現(xiàn)ISecurityInterceptor接口。
在BDF2當(dāng)中,除了通過IRetrivePreAuthenticatedUser接口實(shí)現(xiàn)獲取預(yù)認(rèn)證的登錄用戶外,還可以通過實(shí)現(xiàn)名為ISecurityInterceptor接口,獲取預(yù)認(rèn)證的登錄用戶,完成用戶登錄認(rèn)證。該接口的實(shí)現(xiàn)類,同樣也需要配置到Spring環(huán)境當(dāng)中,該接口的源碼如下:
ISecurityInterceptor接口源碼
package com.bstek.bdf2.core.security;
import org.springframework.security.web.context.HttpRequestResponseHolder;
/**
 * 一個(gè)供開發(fā)人員使用的在登錄、認(rèn)證之前或之后或失敗后需要進(jìn)行業(yè)務(wù)處理的接口,<br>
 *
開發(fā)人員可以根據(jù)需要,有選擇的覆蓋該類中的某個(gè)方法,比如需要在用戶登錄前進(jìn)行一些處理,那么就可覆蓋其中的
beforeLogin方法,<br>
 *
依次類推,使用時(shí),將實(shí)現(xiàn)類配置到spring當(dāng)中即可,系統(tǒng)運(yùn)行時(shí)會(huì)自動(dòng)掃描該抽象類實(shí)現(xiàn)的存在,如果有就會(huì)加載處
理
 * @author jacky.gao
 * @since 2013-1-22
 */
public interface ISecurityInterceptor {
 /**
 * 用戶登錄系統(tǒng)之前進(jìn)行的處理動(dòng)作
 * @param holder 一個(gè)用于包裝HttpRequest/HttpResponse的對(duì)象
 */
 void beforeLogin(HttpRequestResponseHolder holder);
 
 /**
 * 用戶登錄系統(tǒng)成功之后進(jìn)行的處理動(dòng)作
 * @param holder 一個(gè)用于包裝HttpRequest/HttpResponse的對(duì)象
 */
 void loginSuccess(HttpRequestResponseHolder holder);
 
 /**
 * 用戶登錄系統(tǒng)認(rèn)證失敗時(shí)需要處理的動(dòng)作
 * @param holder 一個(gè)用于包裝HttpRequest/HttpResponse的對(duì)象
 */
 void loginFailure(HttpRequestResponseHolder holder);
 
 /**
 * 用戶在訪問系統(tǒng)資源時(shí)(比如訪問某URL),系統(tǒng)安全模塊對(duì)用戶進(jìn)行授權(quán)之前需要處理的動(dòng)作
 * @param holder 一個(gè)用于包裝HttpRequest/HttpResponse的對(duì)象
 */
 void beforeAuthorization(HttpRequestResponseHolder holder);
 /**
 * 用戶在訪問系統(tǒng)資源時(shí)(比如訪問某URL),系統(tǒng)安全模塊對(duì)用戶進(jìn)行授權(quán)成功之后需要處理的動(dòng)作
 * @param holder 一個(gè)用于包裝HttpRequest/HttpResponse的對(duì)象
 */
 void authorizationSuccess(HttpRequestResponseHolder holder);
 /**
 * 用戶在訪問系統(tǒng)資源時(shí)(比如訪問某URL或某模塊),系統(tǒng)安全模塊對(duì)用戶進(jìn)行授權(quán)失敗之后需要處理的動(dòng)作
 * @param holder 一個(gè)用于包裝HttpRequest/HttpResponse的對(duì)象
 */
 void authorizationFailure(HttpRequestResponseHolder holder);
}
對(duì)于我們上述預(yù)認(rèn)證需求,就可以通過編寫這個(gè)接口的實(shí)現(xiàn)類來實(shí)現(xiàn)。因?yàn)檫@個(gè)接口當(dāng)中包含的方法較多,一般情況下,我們只需要實(shí)現(xiàn)這個(gè)接口當(dāng)中的一個(gè)方法即可,所以我們可以通過擴(kuò)展系統(tǒng)當(dāng)中提供的已實(shí)現(xiàn)了ISecurityInterceptor接口的SecurityInterceptorAdapter類來實(shí)現(xiàn),這個(gè)類中提供了關(guān)于ISecurityInterceptor接口所有方法的空實(shí)現(xiàn),所以我們?cè)跀U(kuò)展這個(gè)類時(shí)只需要覆蓋需要的方法即可。比如我們下面的實(shí)現(xiàn)類:
DemoSecurityInterceptor類源碼
package test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.web.context.HttpRequestResponseHolder;
import org.springframework.stereotype.Component;
import com.bstek.bdf2.core.context.ContextHolder;
import com.bstek.bdf2.core.model.DefaultUser;
import com.bstek.bdf2.core.security.SecurityInterceptorAdapter;
import com.bstek.bdf2.core.service.IDeptService;
import com.bstek.bdf2.core.service.IGroupService;
import com.bstek.bdf2.core.service.IPositionService;
@Component
public class DemoSecurityInterceptor extends SecurityInterceptorAdapter {
 @Autowired
 @Qualifier(IDeptService.BEAN_ID)
 private IDeptService deptService;
 @Autowired
 @Qualifier(IPositionService.BEAN_ID)
 private IPositionService positionService;
 @Autowired
 @Qualifier(IGroupService.BEAN_ID)
 private IGroupService groupService;
 @Override
 public void beforeAuthorization(HttpRequestResponseHolder holder) {
 if(ContextHolder.getLoginUser()==null){
 //表示未登錄
 //從其它源讀取登錄信息,比如某些硬件卡中讀取登錄信息等
 DefaultUser user=new DefaultUser("admin");
 user.setCompanyId("bstek");
 //為登錄成功的用戶設(shè)置所在部門、崗位及群組信息
 user.setDepts(deptService.loadUserDepts(user.getUsername()));
 user.setPositions(positionService.loadUserPositions(user.getUsername()));
 user.setGroups(groupService.loadUserGroups(user.getUsername()));
 //為登錄成功的用戶設(shè)置所在部門、崗位及群組信息結(jié)束
 
 //這里的IUser應(yīng)該是從其它源里讀取到的經(jīng)過認(rèn)證的合法的用戶對(duì)象,再轉(zhuǎn)換成IUser對(duì)象實(shí)例
 //接下來需要將這個(gè)user對(duì)象放置到session當(dāng)中及Spring Security的環(huán)境當(dāng)中,以告訴系統(tǒng)已成功登錄
 this.registerLoginInfo(user, holder);
 }
 }
}
上述代碼當(dāng)中比較關(guān)鍵的是最后一句,這個(gè)registerLoginInfo方法位于SecurityInterceptorAdapter類當(dāng)中,它可以把認(rèn)證的用戶對(duì)象放到系統(tǒng)環(huán)境當(dāng)中,用以標(biāo)明用戶已登錄。

兩種獲取預(yù)認(rèn)證用戶的方法比較綜合比較上述兩種方法,我們推薦使用第一種,第一種方法就是為獲取已認(rèn)證的用戶,實(shí)現(xiàn)自動(dòng)登錄而準(zhǔn)備的,所以它看起來更加自然,與系統(tǒng)的結(jié)合性也更好。

還有一種情況,那就是可能我們的系統(tǒng)當(dāng)中存在多種登錄方式,可能根據(jù)用戶訪問的URL后面的參數(shù)來決定跳轉(zhuǎn)到哪個(gè)登錄頁面(我也不清楚什么時(shí)候會(huì)有這種變態(tài)需求),如果是這樣,上面的代碼就需要調(diào)整成下面的樣子:
修改后的SecurityInterceptor實(shí)現(xiàn)類
package test;
import org.springframework.security.web.context.HttpRequestResponseHolder;
import org.springframework.stereotype.Component;
import com.bstek.bdf2.core.context.ContextHolder;
import com.bstek.bdf2.core.security.SecurityInterceptorAdapter;
@Component
public class DemoSecurityInterceptor extends SecurityInterceptorAdapter {
 @Override
 public void beforeAuthorization(HttpRequestResponseHolder holder) {
 if(ContextHolder.getLoginUser()==null){
 //表示未登錄
 String loginType=holder.getRequest().getParameter("loginType");
 if(loginType!=null && loginType.equals("abc")){
 throw new MyLoginException();
 }
 if(loginType!=null && loginType.equals("def")){
 throw new MyLogin1Exception();
 }
 }
 }
}
這里的MyLoginException代碼如下:
MyLoginException
package test;
public class MyLoginException extends RuntimeException {
}
MyLogin1Exception代碼與上述基本一樣,這里不再羅列了。

從上述的代碼中可以看到,一旦發(fā)現(xiàn)要采用某種登錄方式,我們就拋一個(gè)特定異常(比如MyLoginException等),接下來我們就需要來編寫一個(gè)IExceptionHandler接口實(shí)現(xiàn)類,用于捕獲我們之前拋出的異常,一旦捕獲,我們可以進(jìn)行頁面跳轉(zhuǎn)等相關(guān)我們需要的操作(比如跳轉(zhuǎn)到我們需要的登錄頁面等),我們的IExceptionHandler接口實(shí)現(xiàn)類代碼如下:
ExceptionHandler實(shí)現(xiàn)類
package test;
import org.springframework.security.web.context.HttpRequestResponseHolder;
import org.springframework.stereotype.Component;
import com.bstek.bdf2.core.exception.IExceptionHandler;
@Component
public class DemoExceptionHandler implements IExceptionHandler {
 public void handle(HttpRequestResponseHolder holder,
 Throwable exception) {
 try{
 if(exception instanceof MyLoginException){
 holder.getResponse().sendRedirect("/login.jsp");
 }
 if(exception instanceof MyLogin1Exception){
 holder.getResponse().sendRedirect("/login.html");
 }
 }catch(Exception ex){
 throw new RuntimeException(ex);
 }
 }
 public boolean support(Throwable exception) {
 return ((exception instanceof MyLoginException) || (exception instanceof MyLogin1Exception));
 }
}
上述代碼比較簡(jiǎn)單,這里就不再解釋了。同樣這個(gè)實(shí)現(xiàn)編寫完成之后需要配置到Spring環(huán)境當(dāng)中。利用這么一種機(jī)制,大家可以發(fā)揮想象,類似的需求都可以通過這一功能機(jī)制實(shí)現(xiàn),具體就不再啰嗦了。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)