Jeesite单点登录集成Cas另加自定义登录验证
JeeSite是基于多个优秀的开源项目,高度整合封装而成的高效,高性能,强安全性的 开源 Java EE快速开发平台.
Cas主要是用来解决多应用之间统一登陆认证,无需用户在同一公司多应用之间重复登陆。例如阿里巴巴中淘宝、天猫,在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统.
Cas基础
服务端
服务端cas-server-webapp-4.0.0.war,服务器端程序一般不用我们完成,但需要做一点小小的修改,cas的服务器端程序由spring+Spring web flow+cas写成。全部使用spring配置文件。
默认登录用户名密码
去除https
修改第一处: cas/WEB-INF/deployerConfigContext.xml
<bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
p:httpClient-ref="httpClient"/>
增加参数p:requireSecure=”false”,是否需要安全验证,即HTTPS,false为不采用。修改后为:
<bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
p:httpClient-ref="httpClient"
p:requireSecure="false"/>
修改第二处: cas/WEB-INF/spring-configuration/ticketGrantingTicketCookieGenerator.xml
<bean id="ticketGrantingTicketCookieGenerator" class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator"
p:cookieSecure="true"
p:cookieMaxAge="-1"
p:cookieName="CASTGC"
p:cookiePath="/cas" />
参数p:cookieSecure=”true”,同理为HTTPS验证相关,TRUE为采用HTTPS验证,FALSE为不采用https验证。
参数p:cookieMaxAge=”-1”,简单说是COOKIE的最大生命周期,-1为无生命周期,即只在当前打开的IE窗口有效,IE关闭或重新打开其它窗口,仍会要求验证。可以根据需要修改为大于0的数字,比如3600等,意思是在3600秒内,打开任意IE窗口,都不需要验证。
这里把 cookieSecure修改为false就行了
修改客户端应用的web.xml
增加如下filter和mapping(jeesite不修改此处,有其它方式处理)
<filter>
<filter-name>CAS Authentication Filter</filter-name>
<filter-class>
org.jasig.cas.client.authentication.AuthenticationFilter
</filter-class>
<init-param>
<param-name>casServerLoginUrl</param-name>
<param-value>
http://localhost:8080/cas/login
</param-value>
</init-param>
<init-param>
<param-name>renew</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>gateway</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8080</param-value>
</init-param>
</filter>
<filter>
<filter-name>CAS Validation Filter</filter-name>
<filter-class>
org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter
</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>http://localhost:8080/cas</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8080</param-value>
</init-param>
<init-param>
<param-name>useSession</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>redirectAfterValidation</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<filter-class>
org.jasig.cas.client.util.HttpServletRequestWrapperFilter
</filter-class>
</filter>
<filter>
<filter-name>CAS Assertion Thread Local Filter</filter-name>
<filter-class>
org.jasig.cas.client.util.AssertionThreadLocalFilter
</filter-class>
</filter>
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Authentication Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Assertion Thread Local Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Jeesite单点登录集成cas
需要写个自己MyCasRealm.java
package com.thinkgem.jeesite.modules.sys.security;
import java.util.Collection;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cas.CasAuthenticationException;
import org.apache.shiro.cas.CasRealm;
import org.apache.shiro.cas.CasToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.TicketValidationException;
import org.jasig.cas.client.validation.TicketValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.thinkgem.jeesite.common.config.Global;
import com.thinkgem.jeesite.common.utils.SpringContextHolder;
import com.thinkgem.jeesite.common.web.Servlets;
import com.thinkgem.jeesite.modules.sys.entity.Menu;
import com.thinkgem.jeesite.modules.sys.entity.Role;
import com.thinkgem.jeesite.modules.sys.entity.User;
import com.thinkgem.jeesite.modules.sys.security.SystemAuthorizingRealm.Principal;
import com.thinkgem.jeesite.modules.sys.service.SystemService;
import com.thinkgem.jeesite.modules.sys.utils.LogUtils;
import com.thinkgem.jeesite.modules.sys.utils.UserUtils;
public class MyCasRealm extends CasRealm {
private Logger logger = LoggerFactory.getLogger(getClass());
private SystemService systemService;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// return super.doGetAuthenticationInfo(token);
CasToken casToken = (CasToken) token;
if (token == null) {
return null;
}
//获取ticket
String ticket = (String)casToken.getCredentials();
if (!org.apache.shiro.util.StringUtils.hasText(ticket)) {
return null;
}
TicketValidator ticketValidator = ensureTicketValidator();
try {
//回传ticket到服务端验证,验证通过就进入下一行,可以获取登录后的相关信息,否则直接抛异常,即验证不通过
Assertion casAssertion = ticketValidator.validate(ticket, getCasService());
AttributePrincipal casPrincipal = casAssertion.getPrincipal();
String userId = casPrincipal.getName();
User user = getSystemService().getUserByLoginName(userId);
if (user != null) {
Principal p = new Principal(user, false);
PrincipalCollection principalCollection = new SimplePrincipalCollection(p, getName());
return new SimpleAuthenticationInfo(principalCollection, ticket);
} else {
return null;
}
} catch (TicketValidationException e) {
throw new CasAuthenticationException("Unable to validate ticket [" + ticket + "]", e);
}
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
Principal principal = (Principal) getAvailablePrincipal(principals);
// 获取当前已登录的用户
if (!Global.TRUE.equals(Global.getConfig("user.multiAccountLogin"))){
Collection<Session> sessions = getSystemService().getSessionDao().getActiveSessions(true, principal, UserUtils.getSession());
if (sessions.size() > 0){
// 如果是登录进来的,则踢出已在线用户
if (UserUtils.getSubject().isAuthenticated()){
for (Session session : sessions){
getSystemService().getSessionDao().delete(session);
}
}
// 记住我进来的,并且当前用户已登录,则退出当前用户提示信息。
else{
UserUtils.getSubject().logout();
throw new AuthenticationException("msg:账号已在其它地方登录,请重新登录。");
}
}
}
User user = getSystemService().getUserByLoginName(principal.getLoginName());
if (user != null) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
List<Menu> list = UserUtils.getMenuList();
for (Menu menu : list){
if (StringUtils.isNotBlank(menu.getPermission())){
// 添加基于Permission的权限信息
for (String permission : StringUtils.split(menu.getPermission(),",")){
info.addStringPermission(permission);
}
}
}
// 添加用户权限
info.addStringPermission("user");
// 添加用户角色信息
for (Role role : user.getRoleList()){
info.addRole(role.getEnname());
}
// 更新登录IP和时间
getSystemService().updateUserLoginInfo(user);
// 记录登录日志
LogUtils.saveLog(Servlets.getRequest(), "系统登录");
return info;
} else {
return null;
}
}
/**
* 获取系统业务对象
*/
public SystemService getSystemService() {
if (systemService == null){
systemService = SpringContextHolder.getBean(SystemService.class);
}
return systemService;
}
}
修改spring-context-shiro.xml配置文件
原来的:
<property name="loginUrl" value="${adminPath}/login" />
改为:
<property name="loginUrl" value="${cas.server.url}?service=${cas.project.url}${adminPath}/cas" />
其实就是把注释放开
新增配置bean : casRealm
<bean id="casRealm" class="com.thinkgem.jeesite.modules.sys.security.MyCasRealm">
<property name="casServerUrlPrefix" value="${cas.server.url}"/>
<!-- 客户端的回调地址设置,必须和下面的shiro-cas过滤器拦截的地址一致 -->
<property name="casService" value="${cas.project.url}${adminPath}/cas"/>
</bean>
修改Shiro安全管理配置的realm属性
原来Shiro安全管理配置的realm属性:
<property name="realm" ref="systemAuthorizingRealm" />
改为:
<property name="realm" ref="casRealm" />
Jeesite其它
其他的基本没有什么修改的,要改的话就是:jeesite.properties里面的cas.project.url和cas.server.url
Cas通过查询数据库验证用户名、密码正确性(密码非复杂加密,可如:MD5,SHA-1等)
jar包准备
MySQL jdbc驱动:mysql-connector-Java-5.1.13-bin.jar
cas jdbc支持:cas-server-support-jdbc-4.0.0.jar
编辑:WEB-INF\deployerConfigContext.xml,加入数据源:
自定义登录验证(加密规则)
jeesite加密研究
其它方式实现单点登录
使用Cookie解决单点登录 技术点:
1、设置Cookie的路径为setPath(“/”) .即Tomcat的目录下都有效
2、设置Cookie的域setDomain(“.gch.com”);即bbs.gch.com,或是mail.gch.com有效。即跨域。
3、设置Cookie的时间。即使用户不选择在几天内自动登录,也应该保存Cookie以保存在当前浏览器没有关闭的情况下有效。
4、使用Filter自动登录。
jeesite单点登录集成cas
- 修改配置文件 spring-context-shiro.xml 主要修改一下几处:
- 修改对应的java文件