博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
yale-cas 与 shiro进行整合
阅读量:4049 次
发布时间:2019-05-25

本文共 22921 字,大约阅读时间需要 76 分钟。

注意:cas-client.version 3.2.1版本。3.3.0版本单点登出存在问题,还在研究。

一.普通的CAS客户端整合

1.CAS与客户端直接整合

参考资料:

相关配置直接写在web.xml文件中

内容如下:

Archetype Created Web Application
cas oss info
com.gqshao.cas.servlet.InfoServlet
cas oss info
/info
cas oss logout
com.gqshao.cas.servlet.LogoutServlet
cas oss logout
/logout
org.jasig.cas.client.session.SingleSignOutHttpSessionListener
encodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
encodingFilter
/*
REQUEST
FORWARD
CAS Single Sign Out Filter
org.jasig.cas.client.session.SingleSignOutFilter
CAS Single Sign Out Filter
/*
CAS Authentication Filter
org.jasig.cas.client.authentication.AuthenticationFilter
casServerLoginUrl
https://sso.gqshao.com:8443/cas/login
serverName
http://sso.gqshao.com
renew
false
CAS Authentication Filter
/*
REQUEST
FORWARD
CAS Validation Filter
org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter
casServerUrlPrefix
https://sso.gqshao.com:8443/cas
serverName
http://sso.gqshao.com
useSession
true
CAS Validation Filter
/*
REQUEST
FORWARD
CAS HttpServletRequest Wrapper Filter
org.jasig.cas.client.util.HttpServletRequestWrapperFilter
CAS HttpServletRequest Wrapper Filter
/*
CAS Assertion Thread Local Filter
org.jasig.cas.client.util.AssertionThreadLocalFilter
CAS Assertion Thread Local Filter
/*
WEB-INF/views/index.jsp

 

这里面用到一个JSP和两个Servlet在展示项目中存在

 

 

2.CAS通过Spring整合到项目中

参考资料

与上面一种差不多,只不过在web.xml中filter通过spring的DelegatingFilterProxy进行代理,另外需要注意的是bean ticketValidationFilter的属性p:redirectAfterValidation="true"是单点登出的关键

web.xml

 

Archetype Created Web Application
contextConfigLocation
classpath*:/applicationContext-cas.xml
org.springframework.web.context.ContextLoaderListener
org.jasig.cas.client.session.SingleSignOutHttpSessionListener
springServlet
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
/WEB-INF/spring-mvc.xml
1
springServlet
/
cas oss info
com.gqshao.cas.servlet.InfoServlet
cas oss info
/info
cas oss logout
com.gqshao.cas.servlet.LogoutServlet
cas oss logout
/logout
encodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
encodingFilter
/*
REQUEST
FORWARD
CAS Single Sign Out Filter
org.springframework.web.filter.DelegatingFilterProxy
targetBeanName
singleSignOutFilter
CAS Single Sign Out Filter
/*
CAS Authentication Filter
org.springframework.web.filter.DelegatingFilterProxy
targetBeanName
authenticationFilter
CAS Authentication Filter
/*
CAS Validation Filter
org.springframework.web.filter.DelegatingFilterProxy
targetBeanName
ticketValidationFilter
CAS Validation Filter
/*
CAS HttpServletRequest Wrapper Filter
org.springframework.web.filter.DelegatingFilterProxy
targetBeanName
httpServletRequestWrapperFilter
CAS HttpServletRequest Wrapper Filter
/*
CAS Assertion Thread Local Filter
org.springframework.web.filter.DelegatingFilterProxy
targetBeanName
assertionThreadLocalFilter
CAS Assertion Thread Local Filter
/*

 

/src/main/resources/applicationContext-cas.xml

 

配置文件cas.properties

 

cas.server.url=https://sso.gqshao.com:8443/cascas.server.login.url=https://sso.gqshao.com:8443/cas/login#Client Addressserver.name=http://sso.gqshao.com:8090
 

 

 

二.CAS与Shiro进行整合

Shiro的使用,请参考我博客中《》

 

注意,这里没有采用shiro提供的shiro-cas依赖,同样也没有使用到org.apache.shiro.cas.CasFilter,但自己实现的CustomFormAuthenticationFilter参考了CasFilter

 

在基于cas-server-webapp-support 4.0.0(服务器端)cas-client-core(客户端) 4.0.0构建的环境中可以正常使用CasFilter

 

1.web.xml

首先web.xml中分别进行cas和shiro的filter的配置,需要注意的是filter的位置关系。

另外要注意的是cas登陆认证之后返回的地址

 

Archetype Created Web Application
contextConfigLocation
classpath*:/applicationContext-cas-shiro.xml
org.springframework.web.context.ContextLoaderListener
org.jasig.cas.client.session.SingleSignOutHttpSessionListener
springServlet
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
/WEB-INF/spring-mvc.xml
1
springServlet
/
cas oss info
com.gqshao.cas.servlet.InfoServlet
cas oss info
/info
encodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
encodingFilter
/*
REQUEST
FORWARD
CAS Single Sign Out Filter
org.springframework.web.filter.DelegatingFilterProxy
targetBeanName
singleSignOutFilter
CAS Single Sign Out Filter
/*
CAS Validation Filter
org.springframework.web.filter.DelegatingFilterProxy
targetBeanName
ticketValidationFilter
CAS Validation Filter
/*
CAS Authentication Filter
org.springframework.web.filter.DelegatingFilterProxy
targetBeanName
authenticationFilter
CAS Authentication Filter
/*
CAS HttpServletRequest Wrapper Filter
org.springframework.web.filter.DelegatingFilterProxy
targetBeanName
httpServletRequestWrapperFilter
CAS HttpServletRequest Wrapper Filter
/*
CAS Assertion Thread Local Filter
org.springframework.web.filter.DelegatingFilterProxy
targetBeanName
assertionThreadLocalFilter
CAS Assertion Thread Local Filter
/*
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
shiroFilter
/*
REQUEST
FORWARD
60
 

 

2 配置文件applicationContext-cas-shiro.xml

 

/login = authc /logout = logout /static/** = anon /** = user
 

 

 

3.实现类

(1)一个继承org.apache.shiro.web.filter.authc.AuthenticatingFilter的实现类

 

package com.gqshao.cas.authentication;import java.util.Map;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.subject.Subject;import org.apache.shiro.web.filter.authc.AuthenticatingFilter;import org.apache.shiro.web.util.WebUtils;import org.jasig.cas.client.authentication.AttributePrincipal;import org.jasig.cas.client.util.AssertionHolder;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class CustomFormAuthenticationFilter extends AuthenticatingFilter {    private static final Logger log = LoggerFactory.getLogger(CustomFormAuthenticationFilter.class);    public static final String DEFAULT_ERROR_KEY_ATTRIBUTE_NAME = "shiroLoginFailure";    public static final String DEFAULT_LOGINNAME_PARAM = "loginName";    public static final String DEFAULT_PASSWORD_PARAM = "password";    public static final String DEFAULT_REMEMBER_ME_PARAM = "rememberMe";    // 自定义的输入字段    public static final String DEFAULT_CUSTOM_PARAM = "custom";    private String loginNameParam = DEFAULT_LOGINNAME_PARAM;    private String passwordParam = DEFAULT_PASSWORD_PARAM;    private String rememberMeParam = DEFAULT_REMEMBER_ME_PARAM;    private String customParam = DEFAULT_CUSTOM_PARAM;    private String failureKeyAttribute = DEFAULT_ERROR_KEY_ATTRIBUTE_NAME;    public CustomFormAuthenticationFilter() {        setLoginUrl(DEFAULT_LOGIN_URL);    }    @Override    public void setLoginUrl(String loginUrl) {        String previous = getLoginUrl();        if (previous != null) {            this.appliedPaths.remove(previous);        }        super.setLoginUrl(loginUrl);        if (log.isTraceEnabled()) {            log.trace("Adding login url to applied paths.");        }        this.appliedPaths.put(getLoginUrl(), null);    }    /**     * 在访问被拒绝后执行     */    @Override    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {        return executeLogin(request, response);    }    /**     * 创建自定义的令牌     */    @Override    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {        if (request instanceof HttpServletRequest) {            HttpServletRequest httpRequest = (HttpServletRequest) request;            AttributePrincipal principal = (AttributePrincipal) httpRequest.getUserPrincipal();            if (principal == null) {                return null;            }            CustomToken token = new CustomToken();            Map
attrs = principal.getAttributes(); token.setLoginName(attrs.get("loginname").toString()); token.setPassword(attrs.get("password").toString()); token.setSalt(attrs.get("salt").toString()); token.setCustom(attrs.get("custom").toString()); token.setHost(getHost(request)); return token; } return null; } protected boolean isLoginSubmission(ServletRequest request, ServletResponse response) { return (request instanceof HttpServletRequest) && WebUtils.toHttp(request).getMethod().equalsIgnoreCase(POST_METHOD); } protected boolean isRememberMe(ServletRequest request) { return false; } protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { issueSuccessRedirect(request, response); return false; } protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) { setFailureAttribute(request, e); return true; } protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) { String className = ae.getClass().getName(); request.setAttribute(getFailureKeyAttribute(), className); } public String getFailureKeyAttribute() { return failureKeyAttribute; } public void setFailureKeyAttribute(String failureKeyAttribute) { this.failureKeyAttribute = failureKeyAttribute; }}
 

 

(2)Token

 

package com.gqshao.cas.authentication;import org.apache.commons.lang3.StringUtils;import org.apache.shiro.authc.HostAuthenticationToken;import org.apache.shiro.authc.RememberMeAuthenticationToken;public class CustomToken implements HostAuthenticationToken, RememberMeAuthenticationToken {    private String loginName;    private String password;    private String host;    private boolean rememberMe = false;    private String custom;    private String salt;    public CustomToken() {    }    public CustomToken(String loginName, String password, String salt, boolean rememberMe, String host,            String custom) {        this.loginName = loginName;        this.password = password;        this.setSalt(salt);        this.rememberMe = rememberMe;        this.host = host;        this.custom = custom;    }    public Object getPrincipal() {        return getLoginName();    }    public Object getCredentials() {        return getPassword();    }    public String getHost() {        return host;    }    public boolean isRememberMe() {        return rememberMe;    }    public void clear() {        this.loginName = null;        this.host = null;        this.password = null;        this.rememberMe = false;        this.custom = null;        this.setSalt(null);    }    public String toString() {        StringBuilder sb = new StringBuilder();        sb.append(getClass().getName());        sb.append(" - ");        sb.append(loginName);        sb.append(", rememberMe=").append(rememberMe);        if (StringUtils.isNotBlank(host)) {            sb.append(" (").append(host).append(")");        }        return sb.toString();    }    public String getLoginName() {        return loginName;    }    public void setLoginName(String loginName) {        this.loginName = loginName;    }    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;    }    public String getCustom() {        return custom;    }    public void setCustom(String custom) {        this.custom = custom;    }    public void setHost(String host) {        this.host = host;    }    public void setRememberMe(boolean rememberMe) {        this.rememberMe = rememberMe;    }    public String getSalt() {        return salt;    }    public void setSalt(String salt) {        this.salt = salt;    }}

 

 

(3)AuthorizingRealm的实现类,这里其实是为了封装principal(ShiroUser),通过Shiro保存到Session中,后续可以通过SecurityUtils.getSubject().getPrincipal()随时调用,真正的登陆认证通过CAS已经完成。

 

package com.gqshao.cas.authentication;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.realm.AuthorizingRealm;import org.apache.shiro.subject.PrincipalCollection;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import com.gqshao.cas.domain.ShiroUser;public class ShiroDbRealm extends AuthorizingRealm {    private Logger logger = LoggerFactory.getLogger(this.getClass());    @Autowired    public ShiroDbRealm() {        super();        setAuthenticationTokenClass(CustomToken.class);    }    /**     * 认证回调函数,登录时调用.     */    @Override    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)            throws AuthenticationException {        CustomToken token = (CustomToken) authcToken;        ShiroUser root = new ShiroUser();        // TODO: 通过Token与本系统RBAC关联起来        root.setId("自己实现");        root.setLoginName(token.getLoginName());        root.setPassword(token.getPassword());        root.setSalt(token.getSalt());        root.setCustom(token.getCustom());        logger.info("用户[{}]登陆系统, IP:[{}]", token.getLoginName(),token.getHost());        return new SimpleAuthenticationInfo(root, token.getPassword(), getName());    }    /**     * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.     */    @Override    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {        // TODO: 实现鉴权        return null;    }}

 

4.流程说明 和 注意事项

(1)登陆系统的时候,首先由CAS拦截,然后再CAS服务器端登陆认证;

(2)因为Shiro也需要登陆认证,所以CAS认证通过后,请求会被CustomFormAuthenticationFilter拦截,并调用方法onAccessDenied,此时开始走Shiro认证;

(3)首先会在CustomFormAuthenticationFilter的createToken中组装Token。这时可以通过调用httpRequest.getUserPrincipal()或AssertionHolder.getAssertion().getPrincipal();拿到CAS返回信息封装的principal。通过解析principal,组装Token,因为Token可以实现定制,所以这里按需求实现;

(4)Token组装后,会调用ShiroDbRealm的doGetAuthenticationInfo方法进行登录认证,因为实际的登陆认证已经在CAS服务器端实现,所以这里主要是为了shiro的principal,返回SimpleAuthenticationInfo,并且不要写initCredentialsMatcher方法。

 

注意事项

1.CAS的principal()可以通过配置相应Filter后,通过httpRequest.getUserPrincipal(), 或AssertionHolder.getAssertion().getPrincipal()得到;

2.Shiro的可以通过SecurityUtils.getSubject().getPrincipal()得到;

3.当Shiro登陆认证之后,通过httpRequest.getUserPrincipal()得到CAS Principal的方法不可以在用;原因是此时通过httpRequest.getUserPrincipal()调用返回的是org.apache.shiro.web.servlet.ShiroHttpServletRequest.ObjectPrincipal,并且这是一个私有类,并且实现了java.security.Principal。

转载请注明 : http://sgq0085.iteye.com/blog/2003783

4.为了传递中文参数,需要注意两个地方,一个是服务器端的/webapp/WEB-INF/view/jsp/protocol/2.0/casServiceValidationSuccess.jsp页面,需要设置为<%@ page session="false" language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>;另一个地方是客户端的配置文件中Cas20ServiceTicketValidator的encoding属性也要设置为UTF-8。

你可能感兴趣的文章
基于SSH在线考试系统(计算机专业认证考试)——计算机类专业课程设计(毕业设计)
查看>>
Springboot的仓库管理系统——计算机类专业课程设计(毕业设计)
查看>>
刷新页面实现方式总结(HTML,ASP,JS)
查看>>
根据地球上两个地点的经度和纬度,如何获得这两点的距离?
查看>>
COM组件的使用
查看>>
关于文件夹的手动隐藏和恢复
查看>>
JavaScript和Jscript的版本及规范
查看>>
WinCE 对 Java脚本的支持
查看>>
XML学习
查看>>
ASP中LIST控件
查看>>
ASP中按钮触发事件
查看>>
学习:GPIO口模拟I2C
查看>>
展望2007
查看>>
做个男人
查看>>
转:S3C2410 bootloader ----VIVI阅读笔记
查看>>
转:嵌入式系统 Boot Loader 技术内幕
查看>>
ARM 的宏定义
查看>>
SIGN UP BEC2
查看>>
S3C2440中对LED驱动电路的理解
查看>>
《天亮了》韩红
查看>>