最近做项目遇到了多个系统权限用的是shiro框架,需要做成单点登录,虽然shiro为单点登录提供了shiro-cas的方案,但是不太符合我们现有项目的框架,现在和大家分享一下我是如何实现单点登录。整体思路是参考cas。
框架图:
流程介绍
- 用户第一次访问系统A
- 系统A的ssoFilter发现当前session没有用户信息就重定向到登录服务器http://loginservice/login?redirecturl=url
- 登录服务器判断当前用户是否登录如果该用户登录则重定向到用户访问http://redirecturl?sessionid=sessionidg否则调到登录页面
- 系统A的ssoFilter发现当前session没有用户信息但是有参数sessionid,说明该请求是从单点登录服务器跳转过来的并且该用户是已经登录。在ssoFilter中为response添加addcookie(sessionid),且为response写入输出流,该输出流为一个html页面其中有一段js代码是访问系统A中用户访问的url
- 浏览器执行步骤4中返回js,此时访问系统A时携带的cookie中的sessionid是在单点登录系统登录的sessionid,A系统ssoFilter发现该用户已登录允许访问本系统资源。
源码
- 登录服务器login.jsp中的一段代码
<shiro:authenticated>
<%
String redirectUrl = (String) request.getAttribute("redirectUrl");
if (redirectUrl != null && redirectUrl != "") {
response.sendRedirect(redirectUrl+"?SHAREJSESSIONID="+request.getSession().getId());
} else {
response.sendRedirect(request.getContextPath() + "/perm/main/home.do");
}
%>
</shiro:authenticated>
业务系统A中的SSOFilter
public class SSOFilter implements Filter {
private String serverLoginUrl;
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
String reqUrl = httpServletRequest.getScheme() + "://" + httpServletRequest.getServerName() + ":" + httpServletRequest.getServerPort() + httpServletRequest.getContextPath() + httpServletRequest.getRequestURI();
Object username = httpServletRequest.getSession().getAttribute("username");
if (username != null) {
filterChain.doFilter(httpServletRequest, httpServletResponse);
return;
}
String sid = httpServletRequest.getParameter("SHAREJSESSIONID");
if (StringUtils.isNotEmpty(sid)) {
Cookie cookie = new Cookie("SHAREJSESSIONID", sid);
cookie.setPath("/");
httpServletResponse.addCookie(cookie);
String html = "<html><head><script type=\"text/javascript\">location.href='" + reqUrl + "'</script></head><body></body></html>";
byte[] bytes = html.getBytes();
httpServletResponse.setHeader("Content-Type", "text/html;charset=UTF-8");
httpServletResponse.getOutputStream().write(bytes);
httpServletResponse.getOutputStream().flush();
httpServletResponse.getOutputStream().close();
return;
}
httpServletResponse.sendRedirect(serverLoginUrl + "?redirectUrl=" + reqUrl);
}
public void destroy() {
}
public String getServerLoginUrl() {
return serverLoginUrl;
}
public void setServerLoginUrl(String serverLoginUrl) {
this.serverLoginUrl = serverLoginUrl;
}
}
- 业务系统A中的shiro SSOFilter配置
<!-- Shiro主过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="${permisisonPath}/perm/common/login.do"/>
<property name="unauthorizedUrl" value="${permisisonPath}/perm/common/unauth.do"/>
<property name="filters">
<map>
<entry key="SSO" value-ref="SSOFilter"/>
</map>
</property>
</bean>
<bean id="SSOFilter" class="com.xinhe99.permission.sso.SSOFilter">
<property name="serverLoginUrl" value="${permisisonPath}/perm/common/login.do"/>
</bean>
<!-- 权限资源配置 -->
<bean id="filterChainDefinitionsService"
class="com.XXXX.XXX.XXXX.shiro.filterchaindefinitions.SimpleFilterChainDefinitionsService">
<property name="definitions">
<value>
/static/**=anon
/需要登录的请求路径/**=SSO,authc
</value>
</property>
</bean>