客户端配置
增加依赖
maven
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl --> <dependency> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.jasig.cas.client/cas-client-core --> <dependency> <groupId>org.jasig.cas.client</groupId> <artifactId>cas-client-core</artifactId> <version>3.3.3</version> </dependency>
Gradle:
implementation group: 'javax.servlet', name: 'jstl' implementation group: 'org.apache.shiro', name: 'shiro-cas', version: '1.4.0' implementation group: 'org.jasig.cas.client', name: 'cas-client-core', version: '3.3.3'
增加MyFilter,继承CasFilter
public class MyCasFilter extends CasFilter { private static final Logger logger = LoggerFactory.getLogger(MyCasFilter.class); private static final String TICKET_PARAMETER = "ticket"; public static String CUSTOM_SESSION_KEY = "custom_session_key"; private String casServerLoginUrl; private String failureUrl; public MyCasFilter() { } @Override public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { HttpServletRequest httpRequest = (HttpServletRequest) request; String ticket = httpRequest.getParameter(TICKET_PARAMETER); logger.info("CAS login with ticket: {}.", ticket); if (StringUtils.isEmpty(ticket)) { logger.info("SSO ticket not provided, redirect to CAS server."); //redirect to SSO server if ticket not provided WebUtils.issueRedirect(request, response, casServerLoginUrl); return false; } return super.onPreHandle(request, response, mappedValue); } @Override protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException ae, ServletRequest request, ServletResponse response) { //Go to login page if CAS not verified Subject subject = getSubject(request, response); if (subject.isAuthenticated()) { subject.logout(); } try { WebUtils.issueRedirect(request, response, failureUrl); } catch (IOException e) { logger.error("Cannot redirect to failure url : {}", failureUrl, e); } return false; } @Override protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest req, ServletResponse resp) throws Exception { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; WebUtils.issueRedirect(request, response, getSuccessUrl()); return true; } public String getCasServerLoginUrl() { return casServerLoginUrl; } public void setCasServerLoginUrl(String casServerLoginUrl) { this.casServerLoginUrl = casServerLoginUrl; } public String getFailureUrl() { return failureUrl; } @Override public void setFailureUrl(String failureUrl) { this.failureUrl = failureUrl; } }
增加MyRealm,继承CasRealm
public class MyShiroCasRealm extends CasRealm { @Autowired private UserService userService; @Override protected void onInit() { super.onInit(); //SSO doesn't need to check password this.setCredentialsMatcher(new AllowAllCredentialsMatcher()); } /** * 权限认证,为当前登录的Subject授予角色和权限 * 本例中该方法的调用时机为需授权资源被访问时 * 并且每次访问需授权资源时都会执行该方法中的逻辑,这表明本例中默认并未启用AuthorizationCache * 如果连续访问同一个URL(比如刷新),该方法不会被重复调用,Shiro有一个时间间隔(也就是cache时间,在ehcache-shiro.xml中配置),超过这个时间间隔再刷新页面,该方法会被执行 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { return new SimpleAuthorizationInfo(); } /** * 1、CAS认证 ,验证用户身份 * 2、换取token */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) { /** * 可以写自定义的验证逻辑,但是必须调用super.doGetAuthenticationInfo(token); */ return super.doGetAuthenticationInfo(token); } }
增加CasConfiguration类,填写好cas 的server地址、 后台服务器地址、前端服务器地址
public class ShiroCasConfiguration { // cas 的server地址 public String casServerUrlPrefix = ""; // 后台服务器地址 public String backServerUrl = ""; // 前端服务器地址 public String frontServerUrl = ""; // cas 登录页面的地址 public String casLoginUrl = casServerUrlPrefix + "/login"; // casFilter cas 拦截的地址 public final String casFilterUrlPattern = "/cas/loginSuccess"; // 登录成功的地址 public final String loginSuccessUrl = frontServerUrl + "/login.html"; // 登录成功之后的转跳地址,用于校验ticket,换取token public final String serverPath = casLoginUrl + "?service=" + backServerUrl + casFilterUrlPattern; /** * 添加cas验证realm */ public MyShiroCasRealm myShiroCasRealm() { MyShiroCasRealm myShiroCasRealm = new MyShiroCasRealm(); myShiroCasRealm.setCasServerUrlPrefix(casServerUrlPrefix); myShiroCasRealm.setCasService(backServerUrl + casFilterUrlPattern); return myShiroCasRealm; } public FilterRegistrationBean filterRegistrationBean() { FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setFilter(new DelegatingFilterProxy("shiroFilter")); //设置的shiro的拦截器 ShiroFilterFactoryBean bean.addInitParameter("targetFilterLifecycle", "true"); bean.setEnabled(true); bean.addUrlPatterns("/*"); return bean; } // 下面两个配置主要用来开启shiro aop注解支持. 使用代理方式;所以需要开启代码支持; public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator(); proxyCreator.setProxyTargetClass(true); return proxyCreator; } // 开启注解 public AuthorizationAttributeSourceAdvisor attributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; } // CAS过滤器 public CasFilter getCasFilter() { MyCasFilter casFilter = new MyCasFilter(); casFilter.setName("casFilter"); casFilter.setEnabled(true); casFilter.setFailureUrl(casLoginUrl); casFilter.setCasServerLoginUrl(serverPath); casFilter.setSuccessUrl(loginSuccessUrl); return casFilter; } }
修改客户端登录逻辑,如果用户没有登录,引导用户请求CAS服务器登录地址。引导方式包括但不限于以下方式:
(1)Java重定向
response.sendRedirect(response.encodeRedirectURL(targetUrl));
(2)JS重定向
window.location.replace(targetUrl);
(3)a标签点击事件
配置nginx,让多个客户端处于同一个域名下,配置举例方式如下:
server { listen 80; server_name example.com; location ^~ /project1 { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location ^~ /project2 { proxy_pass http://127.0.0.1:8082; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
启动nginx即可。