CAS 4.2.7系列之客户端对接(三)

网友投稿 464 2022-05-30

文章目录

一、 SSO简介

1.1 单点登录定义

1.2 单点登录角色

1.3 单点登录分类

二、 CAS简介

2.1 CAS简单定义

2.2 CAS体系结构

2.3 CAS原理

三、CAS服务端搭建

3.1 CAS支持Http登录配置

3.2 CAS服务端部署运行

四、CAS客户端接入

五、客户端极速接入

一、 SSO简介

1.1 单点登录定义

单点登录(Single sign on),英文名称缩写SSO,SSO的意思就是在多系统的环境中,登录单方系统,就可以在不用再次登录的情况下访问相关受信任的系统。也就是说只要登录一次单体系统就可以。

1.2 单点登录角色

单点登录一般包括下面三种角色:

①用户(多个);

②认证中心(一个);

③Web应用(多个)。

PS:这里所说的web应用可以理解为SSO Client,认证中心可以说是SSO Server。

1.3 单点登录分类

因为http协议是无状态的协议,所以要保持登录状态,必须要存储登录信息,按照存储方式,单点登录实现方式主要可以分为两种。

一种是基于Cookie的,这种比较常见,比如下文介绍的CAS也是基于Cookie的;

另外一种是基于Session的,其实理解起来就是会话共享,只有实现不同子系统之间的会话共享就能实现单点登录,详情可以参考我之前的博客,就是实现会话共享实现单点登录的,链接

二、 CAS简介

2.1 CAS简单定义

CAS(Center Authentication Service)是耶鲁大学研究的一款开源的单点登录项目,主要为web项目提供单点登录实现,属于Web SSO。

CAS Github链接

Apereo CAS官方网站

2.2 CAS体系结构

CAS体系结构分为CAS Server和CAS Client。

CAS Server就是Cas开源的,需要去github下载,然后进行修改;Cas Client

可以是App或者web端的或者PC端,CAS支持多种开发语言,java、php、C#等等

PS:图来自官网,这里简单介绍一下,从图可以看出,CAS支持多种方式的认证,一种是LDAP的、比较常见的数据库Database的JDBC,还有Active Directory等等;支持的协议有Custom Protocol 、 CAS 、 OAuth 、 OpenID 、 RESTful API 、 SAML1.1 、 SAML2.0 等

2.3 CAS原理

下面给出一张来自CAS官方的图片

CAS登录等系统分为CAS Server和CAS Client,下面,我根据我的理解稍微解释一下:

1、用户访问CAS Client请求资源

2、客户端程序做了重定向,重定向到CAS Server

3、CAS Server会对请求做认证,验证是否有TGC(Ticket Granted Cookie,有TGC说明已经登录过,不需要再登录,没有就返回登录页面

4、认证通过后会生成一个Service Ticket返回Cas Client,客户端进行Ticket缓存,一般放在Cookie里,我们称之为TGC(Ticket Granted Cookie)

5、然后Cas Client就带着Ticket再次访问Cas Server,CAS Server进行Ticket验证

6、CAS Server对Ticket进行验证,通过就返回用户信息,用户拿到信息后就可以登录

看到这个过程,我们大概就能理解CAS是怎么实现的,看起来过程挺多的,不过这些过程都是CAS在后台做的。CAS Service和CAS Client通讯基于HttpUrlConnection

注意要点:

TGT(Ticket Granded Ticket),就是存储认证凭据的Cookie,有TGT说明已经通过认证

ST(Service Ticket),是由CAS认证中心生成的一个唯一的不可伪装的票据,用于认证的

没登录过的或者TGT失效的,访问时候也跳转到认证中心,发现没有TGT,说明没有通过认证,直接重定向登录页面,输入账号密码后,再次重定向到认证中心,验证通过后,生成ST,返回客户端保存到TGC

登录过的而且TGT没有失效的,直接带着去认证中心认证,认证中心发现有TGT,重定向到客户端,并且带上ST,客户端再带ST去认证中心验证

三、CAS服务端搭建

3.1 CAS支持Http登录配置

CAS默认是要https的链接才能登录的,不过学习的话是可以先驱动https限制,本博客介绍的是基于Cas4.2.7的,之前改过4.0的,详情见https://blog.csdn.net/u014427391/article/details/82083995

Cas4.2.7和4.0的修改是不一样的,Cas4.2.7版本需要自己编译,是基于Gradle的,不是基于Maven的,觉得麻烦可以下载4.0,因为4.0版本有提供war包,不需要自己编译,下面介绍一下4.2.7版本,怎么支持http登录

需要修改cas4.2.7的cas-server-webapp/WEB-INF/cas.properties,全都改为非安全的

tgc.secure=false warn.cookie.secure=false

1

2

cas-server-webapp/resources/service/HTTPSandIMAPS-10000001.json原来的

"serviceId" : "^(https|imaps)://.*"

1

加上http

"serviceId" : "^(https|imaps|http)://.*"

1

注释cas-server-webapp/WEB-INF/view/jsp/default/ui/casLoginView.jsp页面中校验是否是HTTPS协议的标签块

1

2

3

4

5

6

然后登录就没非安全提示了

3.2 CAS服务端部署运行

然后将war包丢在Tomcat的webapp里,部署启动,默认账号密码casuser/Mellon,cas4.2.7的账号密码是写在cas.properties里的,这个和4.0的不一样

accept.authn.users=casuser::Mellon

1

2

登录成功,当然在项目中,肯定不能这样做,这个需要我们配置jdbc或者加上权限校验等等

单点登出,链接是http://127.0.0.1:8080/cas/logout

四、CAS客户端接入

本博客介绍一下基于SpringBoot的Cas客户端接入,数据库采用mysql,权限控制采用Shiro

maven配置,加上Shiro和CAS的相关jar:

CAS和Shiro的相关版本

1.2.3 1.2.4 1.2.4 3.2.0 1.2.4

1

2

3

4

5

6

7

加上Shiro和cas相关jar

org.apache.shiro shiro-spring ${shiro.spring.version} org.apache.shiro shiro-ehcache ${shiro.encache.version} org.apache.shiro shiro-cas ${shiro.cas.version} org.jasig.cas.client cas-client-core ${cas.version}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

新建一个环境类,保存cas的一些配置链接:

package org.muses.jeeplatform.core; /** *

 * CAS配置环境类 * 
* * @author nicky.ma *
 * 修改记录 * 修改后版本: 修改人: 修改日期: 2019年05月25日 修改内容: * 
*/ public class CASConsts { /* CAS单点登录配置 */ //Cas server地址 public static final String CAS_SERVER_URL_PREFIX = "http://localhost:8080/cas"; //Cas单点登录地址 public static final String CAS_LOGIN_URL = CAS_SERVER_URL_PREFIX +"/login"; //CAS单点登出地址 public static final String CAS_LOGOUT_URL = CAS_SERVER_URL_PREFIX + "/logout"; //对外提供的服务地址 public static final String SERVER_URL_PREFIX = "http://localhost:8081"; //Cas过滤器的urlPattern public static final String CAS_FILTER_URL_PATTERN = "/jeeplatform"; //CAS客户端单点登录跳转地址 public static final String CAS_CLIENT_LOGIN_URL = CAS_LOGIN_URL + "?service="+SERVER_URL_PREFIX+CAS_FILTER_URL_PATTERN; //CAS客户端单点登出 public static final String CAS_CLIENT_LOGOUT_URL = CAS_LOGOUT_URL + "?service="+SERVER_URL_PREFIX+CAS_FILTER_URL_PATTERN; //登录成功地址 public static final String LOGIN_SUCCESS_URL = "/index"; //无权访问页面403 public static final String LOGIN_UNAUTHORIZED_URL = "/403"; }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

ShiroCas配置类:

package org.muses.jeeplatform.config; import org.apache.shiro.cas.CasFilter; import org.apache.shiro.cas.CasSubjectFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.jasig.cas.client.authentication.AuthenticationFilter; import org.jasig.cas.client.session.SingleSignOutFilter; import org.jasig.cas.client.session.SingleSignOutHttpSessionListener; import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter; import org.muses.jeeplatform.core.shiro.ShiroRealm; import org.muses.jeeplatform.web.filter.SysAccessControllerFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletListenerRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.Filter; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import static org.muses.jeeplatform.core.CASConsts.*; /** * @author nicky.ma */ @Configuration public class ShiroConfig { private static final Logger LOG = LoggerFactory.getLogger(ShiroConfig.class); /** * 单点登出监听器 * @return */ @Bean public ServletListenerRegistrationBean singleSignOutHttpSeessionListener(){ ServletListenerRegistrationBean bean = new ServletListenerRegistrationBean(); bean.setListener(new SingleSignOutHttpSessionListener()); bean.setEnabled(true); return bean; } /** * 注册单点登出的过滤器 * @return */ @Bean public FilterRegistrationBean singleSignOutFilter(){ FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setName("singleSignOutFilter"); bean.setFilter(new SingleSignOutFilter()); bean.addUrlPatterns("/*"); bean.setEnabled(true); return bean; } @Bean public FilterRegistrationBean authenticationFilter(){ FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setFilter(new AuthenticationFilter()); bean.addUrlPatterns("/*"); bean.setName("CAS AuthenticationFilter"); bean.addInitParameter("casServerLoginUrl",CAS_SERVER_URL_PREFIX); bean.addInitParameter("serverName",SERVER_URL_PREFIX); return bean; } /** * 单点登录校验 * @return */ @Bean public FilterRegistrationBean validationFilter(){ FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(new Cas20ProxyReceivingTicketValidationFilter()); registrationBean.addUrlPatterns("/*"); registrationBean.setName("CAS Validation Filter"); registrationBean.addInitParameter("casServerUrlPrefix", CAS_SERVER_URL_PREFIX ); registrationBean.addInitParameter("serverName", SERVER_URL_PREFIX ); return registrationBean; } /** * CAS过滤器 * @return */ @Bean public CasFilter getCasFilter(){ CasFilter casFilter = new CasFilter(); casFilter.setName("casFilter"); casFilter.setEnabled(true); casFilter.setFailureUrl(CAS_CLIENT_LOGIN_URL); casFilter.setSuccessUrl(LOGIN_SUCCESS_URL); return casFilter; } /** * 定义ShrioRealm * @return */ @Bean public ShiroRealm myShiroRealm(){ ShiroRealm myShiroRealm = new ShiroRealm(); return myShiroRealm; } /** * Shiro Security Manager * @return */ @Bean public SecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //securityManager.setRealm(myShiroRealm()); securityManager.setSubjectFactory(new CasSubjectFactory()); return securityManager; } /** * ShiroFilterFactoryBean * @param securityManager * @return */ @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager,CasFilter casFilter) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //注册Shrio Security Manager shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setLoginUrl(CAS_CLIENT_LOGIN_URL); shiroFilterFactoryBean.setSuccessUrl(LOGIN_SUCCESS_URL); shiroFilterFactoryBean.setUnauthorizedUrl(LOGIN_UNAUTHORIZED_URL); //添加CasFilter到ShiroFilter Map filters = new HashMap(); filters.put("casFilter",casFilter); shiroFilterFactoryBean.setFilters(filters); //拦截器. Map filterChainDefinitionMap = new LinkedHashMap<>(); //Shiro集成CAS后需要添加该规则 filterChainDefinitionMap.put(CAS_FILTER_URL_PATTERN,"casFilter"); // 配置不会被拦截的链接 顺序判断 filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/upload/**", "anon"); filterChainDefinitionMap.put("/plugins/**", "anon"); filterChainDefinitionMap.put("/code", "anon"); //filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/403", "anon"); //filterChainDefinitionMap.put("/logincheck", "anon"); filterChainDefinitionMap.put("/logout","anon"); filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

CAS 4.2.7系列之客户端对接(三)

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

自定义的CasRealm:

package org.muses.jeeplatform.core.shiro; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.cas.CasRealm; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.muses.jeeplatform.core.entity.admin.User; import org.muses.jeeplatform.service.UserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.PostConstruct; import javax.annotation.Resource; import static org.muses.jeeplatform.core.CASConsts.CAS_FILTER_URL_PATTERN; import static org.muses.jeeplatform.core.CASConsts.CAS_SERVER_URL_PREFIX; /** * @description 基于Shiro框架的权限安全认证和授权 * @author Nicky * @date 2017年3月12日 */ public class ShiroRealm extends CasRealm { Logger LOG = LoggerFactory.getLogger(ShiroRealm.class); /**注解引入业务类**/ @Resource UserService userService; @PostConstruct public void initProperty(){ setCasServerUrlPrefix(CAS_SERVER_URL_PREFIX); //客户端回调地址 setCasService(CAS_SERVER_URL_PREFIX + CAS_FILTER_URL_PATTERN); } /** * 登录信息和用户验证信息验证(non-Javadoc) * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(AuthenticationToken) */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { if(LOG.isInfoEnabled()) { LOG.info("=>执行Shiro权限认证"); } String username = (String)token.getPrincipal(); //得到用户名 String password = new String((char[])token.getCredentials()); //得到密码 User user = userService.findByUsername(username); /* 检测是否有此用户 */ if(user == null){ throw new UnknownAccountException();//没有找到账号异常 } /* 检验账号是否被锁定 */ if(Boolean.TRUE.equals(user.getLocked())){ throw new LockedAccountException();//抛出账号锁定异常 } /* AuthenticatingRealm使用CredentialsMatcher进行密码匹配*/ if(null != username && null != password){ return new SimpleAuthenticationInfo(username, password, getName()); }else{ return null; } } /** * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法(non-Javadoc) * @see AuthorizingRealm#doGetAuthorizationInfo(PrincipalCollection) */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) { if(LOG.isInfoEnabled()) { LOG.info("=>执行Shiro授权"); } String username = (String)pc.getPrimaryPrincipal(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); authorizationInfo.setRoles(userService.getRoles(username)); authorizationInfo.setStringPermissions(userService.getPermissions(username)); return authorizationInfo; } @Override public void clearCachedAuthorizationInfo(PrincipalCollection principals) { super.clearCachedAuthorizationInfo(principals); } @Override public void clearCachedAuthenticationInfo(PrincipalCollection principals) { super.clearCachedAuthenticationInfo(principals); } @Override public void clearCache(PrincipalCollection principals) { super.clearCache(principals); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

五、客户端极速接入

上面例子是比较麻烦的,我们要接入客户端可以常用第三方的starter lib来对接

net.unicon.cas cas-client-autoconfig-support 1.5.0-GA

1

2

3

4

5

6

yaml配置:

cas: server-login-url: http://127.0.0.1:8080/cas/login server-url-prefix: http://127.0.0.1:8080/cas client-host-url: http://127.0.0.1:8081 validation-type: cas # use-session: true

1

2

3

4

5

6

加个Springboot配置类:

package org.muses.jeeplatform.config; import net.unicon.cas.client.configuration.CasClientConfigurerAdapter; import net.unicon.cas.client.configuration.EnableCasClient; import org.jasig.cas.client.authentication.AuthenticationFilter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.Map; @Configuration @EnableCasClient public class CASConfig extends CasClientConfigurerAdapter { private static final String CAS_SERVER_URL_LOGIN = "http://localhost:8080/cas/login"; private static final String SERVER_NAME = "http://localhost:8081/"; // @Override // public void configureAuthenticationFilter(FilterRegistrationBean authenticationFilter) { // super.configureAuthenticationFilter(authenticationFilter); // //authenticationFilter.getInitParameters().put("authenticationRedirectStrategyClass","com.test.CustomAuthRedirectStrategy"); // } @Bean public FilterRegistrationBean filterRegistrationBean(){ FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(new AuthenticationFilter()); registrationBean.addUrlPatterns("/*"); Map initParameters = new HashMap(16); initParameters.put("casServerLoginUrl",CAS_SERVER_URL_LOGIN); initParameters.put("serverName",SERVER_NAME); initParameters.put("ignorePattern","/logoutSuccess/*"); registrationBean.setOrder(1); return registrationBean; } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

web前端

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:212_mysql_innodb_6_lock1
下一篇:String 还能这样性能调优,我直呼内行
相关文章