Shiro整合SSO单点登录系统

前言

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单点登录系统工具类下载

  • 12
    点赞
  • 79
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
# sso-shiro-cas spring下使用shiro+cas配置单点登录,多个系统之间的访问,每次只需要登录一次 ## 系统模块说明 1. cas: 单点登录模块,这里直接拿的是cas的项目改了点样式而已 2. doc: 文档目录,里面有数据库生成语句,采用的是MySQL5.0,数据库名为db_test 3. spring-node-1: 应用1 4. spring-node-2: 应用2 其中node1跟node2都是采用spring + springMVC + mybatis 框架,使用maven做项目管理 ## cas集成说明 1.首先采用的是查数据库的方式来校验用户身份的,在cas/WEB-INF/deployerConfigContext.xml中第135行构建了这个类型 ``` xml ``` 其中QueryDatabaseAuthenticationHandler这个类是自定义构建的,在cas/WEB-INF/lib/cas-jdbc-1.0.0.jar里面,有兴趣的同学可以发编译看下,关于几个属性的说明 1. dataSource: 数据源,配置MySQL的连接信息 2. passwordEncoder: 加密方式,这里用的是MD5 3. sql: sql查询语句,这个语句就是根据用户输入的账号查询其密码 #### 以上就是单点登录管理的主要配置 ## 应用系统的配置node1 1. 应用系统采用shiro做权限控制,并且跟cas集成 2. 在/spring-node-1/src/main/resources/conf/shiro.properties 文件中 ``` properties shiro.loginUrl=http://127.0.0.1:8080/cas/login?service=http://127.0.0.1:8081/node1/shiro-cas shiro.logoutUrl=http://127.0.0.1:8080/cas/logout?service=http://127.0.0.1:8081/node1/shiro-cas shiro.cas.serverUrlPrefix=http://127.0.0.1:8080/cas shiro.cas.service=http://127.0.0.1:8081/node1/shiro-cas shiro.failureUrl=/users/loginSuccess shiro.successUrl=/users/loginSuccess ``` 其中shiro.loginUrl 跟 shiro.logoutUrl的前面是cas验证的地址,后面的是我们应用系统的地址,这样配置的方式是为了在访问我们的应用系统的时候,先到cas进行验证,如果验证成功了,cas将重定向到shiro.successUrl 所表示的地址 3.在/spring-node-1/src/main/resources/conf/shiro.xml 文件中 ``` xml /shiro-cas = casFilter /logout = logoutFilter /users/** = user ``` > 其中shiroFilter这个类主要用于需要拦截的url请求,需要注意的是这个是shiro的拦截,我们还需要配置cas的过滤配置casFilter > casRealm这个类是需要我们自己实现的,主要用于shiro的权限验证,里面的属性说明如下 1. defaultRoles: 默认的角色 2. casServerUrlPrefix: cas地址 3. casService: 系统应用地址 最后我们还需要在/spring-node-1/src/main/webapp/WEB-INF/web.xml 文件中配置相关的过滤器拦截全部请求 ``` xml shiroFilter org.springframework.web.filter.DelegatingFilterProxy targetFilterLifecycle true shiroFilter /* ``` ## 系统运行 1. 端口说明,cas:8080,node1:8081,node2:8082,大家可以采用maven提供的tomcat7插件,配置如下: ``` xml org.apache.tomcat.maven tomcat7-maven-plugin 2.1 8081 UTF-8 tomcat7 /node1 ``` 这样的配置,我们甚至都不需要配置tomcat服务器了,建议这种方式 2.各个模块的访问地址 > cas:http://127.0.0.1:8080/cas > node1:http://127.0.0.1:8081/node1 > node2:http://127.0.0.1:8082/node2 3.访问系统 > 输入 http://127.0.0.1:8081/node1/shiro-cas ,进入cas验证 > 输入用户名 admin,密码 admin@2015,验证成功后将会重定向到http://127.0.0.1:8081/node1//users/loginSuccess ,也就是node1系统的主页,里面的节点2代表的是node2系统的主页,你会发现我们不需要登录到node2系统就能访问其中的系统

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值