前言
shiro是一个非常强大的权限管理框架,关于shiro与cas整合的示例有很多,但是我们平时开发的时候,很多公司并不是使用cas来做SSO的,而是自己公司会用自己开发的。本文就主要针对这种方式的整合。
新增SSO相关的properties
#sso服务器登录地址,service参数表示登录成功后要跳转的地址
ssoServiceUrl=http://www.authserver.com/auth/login?service=http://www.client.com/user/login
#sso的token的校验地址
tokenValidateUrl=http://www.authserver.com:9999/auth/validatetoken
#应用名称标识
microServiceId=app
#redis相关信息
JedisUrl=198.168.1.101
JedisPort=6379
JedisPassword=666666
修改spring-shiro.xml
<bean id="shiroSecurityFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="${ssoServiceUrl}" />
<!-- <property name="successUrl" value="/user/index" /> -->
<property name="filters">
<map>
<entry key="authc" value-ref="ssoFilter"/>
</map>
</property>
<property name="filterChainDefinitions">
<value>
/kaptcha.jpg=anon
/assets/** = anon
/images/** = anon
/javascripts/** = anon
/stylesheets/** = anon
/user/login = anon
/httpserver/* = anon
/** = authc
</value>
</property>
</bean>
<bean id="ssoFilter" class="com.demo.web.sso.SSOFilter"/>
自定义ssoFilter
/**
* sso自定义过滤器
*/
public class SSOFilter implements Filter{
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
{
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
String token = request.getParameter("token");
PropertiesTool propertiesTool = PropertiesTool.getInstance();
String ssoServiceUrl = propertiesTool.getValue("ssoServiceUrl");
String tokenValidateUrl = propertiesTool.getValue("tokenValidateUrl");
if(null == token) {
Cookie cookie = CookieUtil.getCookieByName(request, "token");
if(null != cookie) {
token = cookie.getValue();
}
}
if(null == token) {
//没有token,重定向至sso server登录页
response.sendRedirect(ssoServiceUrl);
}else {
String urlString = request.getRequestURI();
if(urlString.endsWith("/logout")) {
String JedisUrl = propertiesTool.getValue("JedisUrl");
String JedisPort = propertiesTool.getValue("JedisPort");
Jedis jedis = new Jedis(JedisUrl, Integer.parseInt(JedisPort));
jedis.del(token.getBytes());
jedis.close();
SecurityUtils.getSubject().logout();
response.sendRedirect(ssoServiceUrl);//重定向至sso server登录页
return;
}
//有token,去sso server验证token的有效性
Map<String, String> map = new HashMap<>();
map.put("token", token);
String result = HttpClientUtil.doGet(tokenValidateUrl, map);
if(StringUtils.isNotBlank(result)){//验证成功,跳转至首页,并从redis中获取权限
SystemSession.setUser(SSOTokenUtil.getToken(request));//设置系统session(把用户信息保存在ThreadLocal中)
Cookie cookie = new Cookie("token", token);
cookie.setPath("/");
response.addCookie(cookie);
filterChain.doFilter(request, response);
} else{
response.sendRedirect(ssoServiceUrl);//验证失败,重定向至sso server登录页
}
}
}
}
public void destroy() {
}
自定义shiro的realm
在spring-shiro.xml中添加:
<bean id="myRealm" class="com.demo.web.realm.MyRealm"/>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm" />
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<bean
class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
</bean>
realm类:
public class MyRealm extends AuthorizingRealm{
public MyRealm() {
setName("myRealm");
HashedCredentialsMatcher hcm = new HashedCredentialsMatcher();
//使用SHA-512 加密
hcm.setHashAlgorithmName(Sha512Hash.ALGORITHM_NAME);
setCredentialsMatcher(hcm);
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
SysUser user = SystemSession.getUser();
if (user != null) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
List<Menu> list = user.getRoles().get(0).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 (SysRole role : user.getRoles()) {
info.addRole(role.getEnglishName());
}
return info;
} else {
return null;
}
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
//获取user信息
SysUser user = SystemSession.getUser();
if (user != null) {
if (user.getIsEnable()!=1) {
throw new AuthenticationException("msg:该帐号已禁止登录.");
}
return new SimpleAuthenticationInfo(new Principal(user), Constants.CREDENTIALS, getName());
}
return null;
}
public static class Principal implements Serializable {
private static final long serialVersionUID = 1L;
private String id; // 编号
private String loginName; // 登录名
private String name; // 姓名
private List<String> roleIdList;
public List<String> getRoleIdList() {
return roleIdList;
}
public void setRoleIdList(List<String> roleIdList) {
this.roleIdList = roleIdList;
}
public Principal(SysUser user) {
this.id = user.getId();
this.loginName = user.getUsername();
this.name = user.getCharacterName();
this.roleIdList=user.getRoleIdList();
}
public String getId() {
return id;
}
public String getLoginName() {
return loginName;
}
public String getName() {
return name;
}
@Override
public String toString() {
return id;
}
}
}
由于shrio权限要做登录校验,但我们的登录已交给sso处理,这里只需要保证shiro的前后校验能通过就行,把password改成常量,并取消原来的加盐。CREDENTIALS=”6bf968f0c7b39ddbb7c8aa836b74d092060ed9b85b620a7fb088ecc48c6b3efd696bbd820f74c14f66c80c86c557c4bfda51b8a3743d3d568cc5c08410b7bb6a”;
修改index
@RequestMapping("index")
public String index(ModelMap model, String mainFrame, HttpServletRequest request) {
SysUser currentUser = SSOTokenUtil.getToken(request);
List<Menu> menus = currentUser.getRoles().get(0).getMenuList();
List<Menu> sysMenus = new ArrayList<>();
for(Menu menu : menus){
if(currentUser.getRemarks().equals(menu.getSysCode())){
sysMenus.add(menu);
}
}
//设置用户登录信息
UsernamePasswordToken token = new UsernamePasswordToken(
currentUser.getUsername(), Constants.PASSWORD);
SecurityUtils.getSubject().login(token);
model.addAttribute("menus", sysMenus);
model.addAttribute("currentUser", currentUser);
return "index";
}
PASSWORD= “lvadmin”,是CREDENTIALS对应的明文。
文中所用到的一些SSOFilter,SystemSession,SSOTokenUtil等原码,以附件的形式提供下载。
Shiro整合SSO单点登录系统工具类下载