单点登录:Single Sign On,简称SSO,SSO使得在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
CAS框架:CAS(Central Authentication Service)是实现SSO单点登录的框架。
逻辑关系图:(注:图为转载)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d2I5tyKA-1651236640645)(https://blog.csdn.net/qq_25223941/article/details/78316108)]
分析:
1.图中用户访问cas客户端;
2.需要登录时,重定向到Cas-Server(Cas服务),其中service为Cas-Client路径
(用于Cas-Server执行完后返回到指定路径);
3.cas-server认证用户信息,并生成一个ticket返回给用户;
4.用户使用此ticket访问Cas-Client(连接了Cas-Server具体应用);
5.Cas-Client使用ticket再次访问Cas-Server进行认证;
6.认证成功后返回Server指定路径到Cas-Client,并返回具体登录用户信息,流程结束。
这是原生Cas的一套流程,那么我们需要集成到已经使用Shiro框架的应用中,如何进行无缝衔接呢?
具体流程:
对于Cas-Server就不多说了,官网下载下来后,修改验证用户信息的配置(从数据库中读取数据进行
身份认证),修改配置文件-deployerConfigContext:
(其中deployerConfigContext.xml文件是CAS专门提出来的供用户修改的配置,其他配置不建议修改)
[html] view plain copy
-
<beanid=“primaryAuthenticationHandler”
-
class=“org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler”>
-
<propertyname="dataSource"ref=“dataSource”/>
-
<propertyname="sql"value=“selectpasswordfromuserwhereuserName=”/>
-
</bean>
-
<beanid="dataSource"class=“org.springframework.jdbc.datasource.DriverManagerDataSource”>
-
<propertyname="driverClassName"value=“com.mysql.jdbc.Driver”/>
-
<propertyname="url"value=“jdbc:mysql://127.0.0.1:3306/**characterEncoding=utf8”/>
-
<propertyname=“username"value=”**"/>
-
<propertyname=“password"value=”**"/>
-
</bean>
注意:其中需要引入包cas-server-support-jdbc-4.0.0.jar(版本号极为重要,与cas-server-core版本一致即可)
还需要引入mysql-connector-java-5.1.34.jar(连接mysql)
连接Cas-Server的使用了Shiro的应用项目连接的整体操作流程,如下:
1.首先需要引入包shiro-cas这个包是shiro和cas连接的通道;
2.修改项目中shiroRealm(自定义的登录验证类,里面包含登录认证、权限认证)
[java] view plain copy
-
/**
-
*shiro登录实现类
-
*
-
*/
-
//重点是集成CasRealm
-
publicclassShiroRealmextendsCasRealm{
-
privateLoggerlog=LoggerFactory.getLogger(ShiroRealm.class);
-
privateTicketValidatorticketValidator;
-
protectedTicketValidatorensureTicketValidator()
-
{
-
if(ticketValidator==null)
-
ticketValidator=createTicketValidator();
-
returnticketValidator;
-
}
-
@Override
-
protectedAuthenticationInfodoGetAuthenticationInfo(AuthenticationTokenauthcToken)throwsAuthenticationException{
-
CasTokencasToken=(CasToken)authcToken;
-
if(authcToken==null)
-
returnnull;
-
Stringticket=(String)casToken.getCredentials();
-
TicketValidatorticketValidator=ensureTicketValidator();
-
try
-
{
-
AssertioncasAssertion=ticketValidator.validate(ticket,getCasService());
-
AttributePrincipalcasPrincipal=casAssertion.getPrincipal();
-
StringuserId=casPrincipal.getName();
-
log.debug(“Validateticket:{}inCASserver:{}toretrieveuser:{}”,newObject[]{
-
ticket,getCasServerUrlPrefix(),userId
-
});
-
Map<String,Object>attributes=casPrincipal.getAttributes();
-
casToken.setUserId(userId);
-
StringrememberMeAttributeName=getRememberMeAttributeName();
-
StringrememberMeStringValue=(String)attributes.get(rememberMeAttributeName);
-
booleanisRemembered=rememberMeStringValue!=null&&Boolean.parseBoolean(rememberMeStringValue);
-
if(isRemembered)
-
casToken.setRememberMe(true);
-
/**此处是封装用户信息
-
sUsrsu=newsUsr();
-
su.setUsrCde(userId);
-
sUsrsusr=isUsrService.findByCode(su);
-
AccessTokenInfoatInfo=newAccessTokenInfo();
-
atInfo.setUsrCde(userId);
-
//获取apikey
-
AccessTokenInfoati=accessTokenInfoService.selectOneByObject(atInfo);
-
//构建ShiroUserAccount
-
ShiroUserAccountsua=newShiroUserAccount(susr,ati);
-
*/
-
Listprincipals=CollectionUtils.asList(newObject[]{
-
sua,attributes
-
});
-
PrincipalCollectionprincipalCollection=newSimplePrincipalCollection(principals,getName());
-
returnnewSimpleAuthenticationInfo(principalCollection,ticket);
-
}
-
catch(TicketValidationExceptione)
-
{
-
thrownewCasAuthenticationException((newStringBuilder()).append(“Unabletovalidateticket[”).append(ticket).append(“]”).toString(),e);
-
}
-
}
-
@Override
-
protectedAuthorizationInfodoGetAuthorizationInfo(PrincipalCollectionprincipal){
-
SimpleAuthorizationInfoinfo=newSimpleAuthorizationInfo();
-
//获取登录用户的Shiro对象—主体身份信息(验权)
-
ShiroUserAccountshiroUser=(ShiroUserAccount)principal.getPrimaryPrincipal();
-
//断言,若对象为空则直接抛出异常
-
Assert.notNull(shiroUser,“找不到principal中的SessionVariable—shiroUser”);
-
//添加用户拥有的role
-
addRoles(info,shiroUser);
-
addPermissions(info,shiroUser);
-
returninfo;
-
}
-
}
3.配置shiro.xml文件:
shiroRealm:
[html] view plain copy
- <beanid="shiroRealm"class=“com.**.ShiroRealm”>
- <propertyname="casServerUrlPrefix"value=“http://127.0.0.1:8080/SSO”/>
- <propertyname="casService"value=“http://127.0.0.1:8585/**/shiro-cas”/>
- </bean>
shiroFilter:
[html] view plain copy
-
<beanid="shiroFilter"class=“org.apache.shiro.spring.web.ShiroFilterFactoryBean”>
-
<propertyname="securityManager"ref=“securityManager”/>
-
<spanstyle=“white-space:pre;”></span>
-
<propertyname="loginUrl"value=“http://127.0.0.1:8080/SSO/loginservice=http://127.0.0.1:8585/**/shiro-cas”/>
-
<propertyname=“successUrl"value=”"/>
-
<propertyname=“unauthorizedUrl"value=”/"/>
-
<propertyname=“filters”>
-
<spanstyle=“white-space:pre;”></span><map>
-
<spanstyle=“white-space:pre;”></span><entrykey="casFilter"value-ref=“casFilter”/>
-
<spanstyle=“white-space:pre;”></span>
-
<spanstyle=“white-space:pre;”></span><entrykey="logoutFilter"value-ref=“logoutFilter”/>
-
<spanstyle=“white-space:pre;”></span></map>
-
</property>
-
<propertyname=“filterChainDefinitions”>
-
<value>
-
/shiro-cas=casFilter
-
/person/**=authc
-
</value>
-
</property>
-
</bean>
casFilter:
[html] view plain copy
- <beanid="casFilter"class=“org.apache.shiro.cas.CasFilter”>
- <propertyname="failureUrl"value=“http://127.0.0.1:8080/SSO/loginservice=http://127.0.0.1:8585/themis_front/shiro-cas”/>
- <propertyname=“successUrl"value=”/themis/ReviewQuery"/>
- </bean>
logoutFilter:
[html] view plain copy
- <beanid="logoutFilter"class=“org.apache.shiro.web.filter.authc.LogoutFilter”>
- <propertyname="redirectUrl"value=“http://127.0.0.1:8080/SSO/logoutservice=http://127.0.0.1:8585/themis_front/shiro-cas”/>
- </bean>
cas针对subject工厂配置:
[html] view plain copy
-
<beanid="casSubjectFactory"class=“org.apache.shiro.cas.CasSubjectFactory”></bean>
-
<beanid="securityManager"class=“org.apache.shiro.web.mgt.DefaultWebSecurityManager”>
-
<propertyname="realm"ref=“shiroRealm”/>
-
<propertyname="sessionManager"ref=“sessionManager”/>
-
<propertyname="cacheManager"ref=“shiroCacheManager”/>
-
<propertyname="subjectFactory"ref=“casSubjectFactory”></property>
-
</bean>
如下是shiro剩余的基本配置:
[html] view plain copy
-
<beanid="sessionManager"class=“org.apache.shiro.web.session.mgt.DefaultWebSessionManager”>
-
<propertyname="globalSessionTimeout"value=“1800000”/>
-
<propertyname="sessionDAO"ref=“shiroSessionDao”/>
-
<propertyname="sessionIdCookie"ref=“sharesession”/>
-
<propertyname="sessionValidationSchedulerEnabled"value=“true”/>
-
</bean>
-
<beanid="sharesession"class=“org.apache.shiro.web.servlet.SimpleCookie”>
-
<constructor-argname="name"value=“SHAREJSESSIONID”/>
-
<propertyname="maxAge"value=“2592000”/>
-
</bean>
-
<beanid="shiroSessionDao"class=“org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO”/>
-
<beanid="shiroCacheManager"class=“org.apache.shiro.cache.MemoryConstrainedCacheManager”/>
-
<beanid="lifecycleBeanPostProcessor"class=“org.apache.shiro.spring.LifecycleBeanPostProcessor”/>
-
<beanclass="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"depends-on=“lifecycleBeanPostProcessor”>
-
<propertyname="proxyTargetClass"value=“true”/>
-
</bean>
-
<beanclass=“org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor”>
-
<propertyname="securityManager"ref=“securityManager”/>
-
</bean>