saiku单点登录方案
Table of Contents
基本思路
公司已经有了SSO 单点登陆方案。登陆成功后,浏览器中会有加密过的cookie信息。Saiku的安全机制是基于Spring security的。在Spring security中有Pre-Authentication机制。 我们要做的就是实现Pre-Authentication,通过读取公司SSO系统中的cookie信息获得登陆人,并且编写角色赋权的逻辑,然后传给spring security。这样saiku系统就会使用我们传给它的 角色来确定显示哪些cube。
修改Spring 配置信息
修改 /src/main/webapp/WEB-INF/applicationContext-spring-security.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<import resource="spring-security.xml"/>
</beans>
spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<bean id="ssoHeaderFilter"
class="com.standino.ipc.dashboard.interceptor.SSORequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="REMOTE_USER"/>
<!-- fall back to other authentication providers is OAM SSO is not there -->
<property name="exceptionIfHeaderMissing" value="false" />
<!-- hard code a testUserId for local tests -->
<property name="testUserId" value="admin" />
<property name="authenticationManager" ref="authenticationManager" />
<property name="authenticationDetailsSource">
<bean class="com.standino.ipc.dashboard.security.HeaderAuthenticationDetails">
<!-- look for the request header set by the webgate and map to local
roles -->
<property name="roleHeaderName" value="ROLES" />
<property name="userRoles2GrantedAuthoritiesMapper">
<bean
class="org.springframework.security.core.authority.mapping.SimpleAttributes2GrantedAuthoritiesMapper">
<property name="convertAttributeToUpperCase" value="true" />
</bean>
</property>
<!-- setup a testing role if not deployed with a webgate - this only
applies if ENV_NAME != uat/prod -->
<property name="testingRoles">
<set>
<value>ROLE_ADMIN</value>
<value>ROLE_USER</value>
</set>
</property>
<!-- all available roles for this application -->
<property name="allRoles">
<set>
<value>ROLE_ADMIN</value>
<value>ROLE_USER</value>
</set>
</property>
</bean>
</property>
</bean>
<bean id="preauthAuthProvider"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService" ref="preAuthenticatedUserDetailsService" />
</bean>
<!-- magically map the user header to a valid user object -->
<bean id="preAuthenticatedUserDetailsService"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService" />
<bean id="securityContextHolderAwareRequestFilter"
class="org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter" />
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="preauthAuthProvider" />
<!-- this is an example of alternate user authentication providers, although
we only have the PRE_AUTH_FILTER defined above, so it isn't used. -->
<security:authentication-provider>
<security:user-service>
<security:user authorities="ROLE_USER" name="guest" password="guest" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
<bean id="userDetailsService"
class="com.standino.ipc.dashboard.security.UserDetailsServices" />
</beans>
实现 java 类
SSORequestHeaderAuthenticationFilter.java
/**
* Handles for SSO request headers to create Authorization ids.
* Optional operations can be assigned by setting the {@link SSOLoginHandler};
* for example, to create corresponding user accounts if the user doesn't exist.
*/
public class SSORequestHeaderAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {
private static final Logger log = Logger.getLogger(SSORequestHeaderAuthenticationFilter.class);
private String principalRequestHeader = "REMOTE_USER";
/**
* Configure a value in the applicationContext-security for local tests.
*/
private String testUserId = "admin";
/**
* Configure whether a missing SSO header is an exception.
*/
private boolean exceptionIfHeaderMissing = false;
/**
* Read and return header named by <tt>principalRequestHeader</tt> from Request
*
* @throws PreAuthenticatedCredentialsNotFoundException
* if the header is missing and
* <tt>exceptionIfHeaderMissing</tt> is set to <tt>true</tt>.
*/
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
String ssoCookieName = "erp";
String ssoAuthenticationKey="2099";
String erpUser = "没有设置";
Cookie cookie = CookieUtil.getCookie(request, ssoCookieName);
if (cookie != null) {
erpUser = SSOUtil.getErpUserName(cookie.getValue(), ssoAuthenticationKey);
}
// System.out.println("获得erp用户 =="+erpUser);
String principal = request.getHeader(principalRequestHeader);
if (principal == null) {
if (exceptionIfHeaderMissing) {
throw new PreAuthenticatedCredentialsNotFoundException(principalRequestHeader
+ " header not found in request.");
} if (StringUtils.isNotBlank(testUserId)) {
// log.warn("spring configuration has a test user id " + testUserId);
principal = testUserId;
} else if (request.getSession().getAttribute("session_user") != null) {
// A bit of a hack for testers - allow the principal to be
// obtained by session. Must be set by a page with no security filters enabled.
// should remove for production.
principal = (String) request.getSession().getAttribute("session_user");
}
}
// also set it into the session, sometimes that's easier for jsp/faces
// to get at..
request.getSession().setAttribute("session_user", principal);
return principal;
}
/**
* Credentials aren't applicable here for OAM WebGate SSO.
*/
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "password_not_applicable";
}
public void setPrincipalRequestHeader(String principalRequestHeader) {
Assert.hasText(principalRequestHeader, "principalRequestHeader must not be empty or null");
this.principalRequestHeader = principalRequestHeader;
}
public void setTestUserId(String testId) {
if (StringUtils.isNotBlank(testId)) {
this.testUserId = testId;
}
}
/**
* Exception if the principal header is missing. Default <tt>false</tt>
* @param exceptionIfHeaderMissing
*/
public void setExceptionIfHeaderMissing(boolean exceptionIfHeaderMissing) {
this.exceptionIfHeaderMissing = exceptionIfHeaderMissing;
}
public void setAuthenticationDetailsSource(AuthenticationDetailsSource source) {
// log.info("testing authenticationDetailsSource set " + source);
super.setAuthenticationDetailsSource(source);
}
}
HeaderAuthenticationDetails.java
public class HeaderAuthenticationDetails extends AuthenticationDetailsSourceImpl {
private static final Logger log = Logger.getLogger(HeaderAuthenticationDetails.class);
/**
* Can be setup in applicationContext-security if the ROLES header value is
* not found.
*/
private Set<String> testingRoles = new HashSet<String>();
/**
* Security principal will only contain roles from "allRoles" - letting us
* cut down the irrelevant values setup by the webgate SSO header.
*/
protected Set<String> allRoles = new HashSet<String>();
/**
* setup in applicationContext-security
*/
private String roleHeaderName = "ROLES";
protected Attributes2GrantedAuthoritiesMapper grantedAuthoritiesMapper
= new SimpleAttributes2GrantedAuthoritiesMapper();
public HeaderAuthenticationDetails() {
super.setClazz(PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails.class);
}
/**
* Build the authentication details object. If the specified authentication
* details class implements {@link MutableGrantedAuthoritiesContainer}, a
* list of pre-authenticated Granted Authorities will be set based on the
* roles for the current user.
*/
public Object buildDetails(Object context) {
Object result = super.buildDetails(context);
List<GrantedAuthority> userGas = new ArrayList<GrantedAuthority>();
if (result instanceof MutableGrantedAuthoritiesContainer) {
Collection<String> userRoles = getUserRoles(context, allRoles);
userGas = grantedAuthoritiesMapper.getGrantedAuthorities(userRoles);
((MutableGrantedAuthoritiesContainer) result).setGrantedAuthorities(userGas);
}
return result;
}
/**
* Allows the roles of the current user to be determined from the context
* object
*
* @param context
* the context object (HttpRequest, PortletRequest etc)
* @param mappableRoles
* the possible roles determined by the
* MappableAttributesRetriever
* @return Collection<string> subset of mappable roles current user has.
*/
protected Collection<String> getUserRoles(Object context, Set<String> mappableRoles) {
ArrayList<String> requestRoles = new ArrayList<String>();
if (((HttpServletRequest) context).getHeader(roleHeaderName) != null) {
String[] roles = ((HttpServletRequest) context).getHeader(roleHeaderName).split(",");
for (int i = 0; i < roles.length; i++) {
if (mappableRoles.contains(roles[i])) {
requestRoles.add(roles[i]);
}
}
} else if ( testingRoles != null) {
log.warn("Failed to retrieve Roles from Header, for debug purposes set to testingRole"+testingRoles);
requestRoles.addAll(testingRoles);
} else {
log.warn("Failed to retrieve Roles from Header, setup as 'user' role.");
requestRoles.add("USER");
}
// add them to the session for convenience
((HttpServletRequest) context).getSession().setAttribute("ROLES", requestRoles);
return requestRoles;
}
/**
* @param mapper
* The Attributes2GrantedAuthoritiesMapper to use
*/
public void setUserRoles2GrantedAuthoritiesMapper(Attributes2GrantedAuthoritiesMapper mapper) {
grantedAuthoritiesMapper = mapper;
}
/**
* All available roles for this application
*
* @param allRoles
*/
public void setAllRoles(Set<String> allRoles) {
this.allRoles = allRoles;
}
/**
* @param roleHeaderName
*/
public void setRoleHeaderName(String roleHeaderName) {
this.roleHeaderName = roleHeaderName;
}
/**
* @param testingRole
*/
public void setTestingRoles(Set<String> testingRole) {
this.testingRoles = testingRole;
}
}
UserDetailsServices.java
public class UserDetailsServices implements UserDetailsService {
/**
* Logger for this class
*/
private static final Logger logger = Logger.getLogger(UserDetailsServices.class);
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException {
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
authorities.add(new GrantedAuthorityImpl("ROLE_ADMIN"));
authorities.add(new GrantedAuthorityImpl("ROLE_USER"));
return new UserToken("admin","admin" , authorities);
}
}