当前位置: 移动技术网 > IT编程>开发语言>Java > spring boot 1.5.4 集成shiro+cas,实现单点登录和权限控制

spring boot 1.5.4 集成shiro+cas,实现单点登录和权限控制

2019年07月22日  | 移动技术网IT编程  | 我要评论

1.添加maven依赖(先安装好cas-server-3.5.2,安装步骤请查看本文参考文章)

    <dependency>
      <groupid>org.apache.shiro</groupid>
      <artifactid>shiro-spring</artifactid>
      <version>1.2.4</version>
    </dependency>
    <dependency>
      <groupid>org.apache.shiro</groupid>
      <artifactid>shiro-ehcache</artifactid>
      <version>1.2.4</version>
    </dependency>
    <dependency>
      <groupid>org.apache.shiro</groupid>
      <artifactid>shiro-cas</artifactid>
      <version>1.2.4</version>
    </dependency>    

2.启动累添加@servletcomponentscan注解

@springbootapplication
public class application {
  /**
   * main function
   * @param args params
   */
  public static void main(string[] args){
    configurableapplicationcontext context = springapplication.run(application.class,args);
    //test if a xml bean inject into springcontext successful
    user user = (user)context.getbean("user1");
    system.out.println(jsonobject.tojsonstring(user));
  }
}

 3.配置shiro+cas

package com.hdwang.config.shirocas;
import com.hdwang.dao.userdao;
import org.apache.shiro.cache.ehcache.ehcachemanager;
import org.apache.shiro.cas.casfilter;
import org.apache.shiro.cas.cassubjectfactory;
import org.apache.shiro.spring.lifecyclebeanpostprocessor;
import org.apache.shiro.spring.security.interceptor.authorizationattributesourceadvisor;
import org.apache.shiro.spring.web.shirofilterfactorybean;
import org.apache.shiro.web.filter.authc.logoutfilter;
import org.apache.shiro.web.mgt.defaultwebsecuritymanager;
import org.jasig.cas.client.session.singlesignoutfilter;
import org.jasig.cas.client.session.singlesignouthttpsessionlistener;
import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springframework.aop.framework.autoproxy.defaultadvisorautoproxycreator;
import org.springframework.beans.factory.annotation.qualifier;
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 org.springframework.core.ordered;
import org.springframework.web.filter.delegatingfilterproxy;
import javax.servlet.filter;
import javax.servlet.annotation.weblistener;
import java.util.hashmap;
import java.util.linkedhashmap;
import java.util.map;
/**
 * created by hdwang on 2017/6/20.
 * shiro+cas 配置
 */
@configuration
public class shirocasconfiguration {
  private static final logger logger = loggerfactory.getlogger(shirocasconfiguration.class);
  // cas server地址
  public static final string casserverurlprefix = "https://localhost:8443/cas";
  // cas登录页面地址
  public static final string casloginurl = casserverurlprefix + "/login";
  // cas登出页面地址
  public static final string caslogouturl = casserverurlprefix + "/logout";
  // 当前工程对外提供的服务地址
  public static final string shiroserverurlprefix = "http://localhost:8081";
  // casfilter urlpattern
  public static final string casfilterurlpattern = "/cas";
  // 登录地址
  public static final string loginurl = casloginurl + "?service=" + shiroserverurlprefix + casfilterurlpattern;
  // 登出地址
  public static final string logouturl = caslogouturl+"?service="+shiroserverurlprefix;
  // 登录成功地址
  public static final string loginsuccessurl = "/home";
  // 权限认证失败跳转地址
  public static final string unauthorizedurl = "/error/403.html";
  @bean
  public ehcachemanager getehcachemanager() {
    ehcachemanager em = new ehcachemanager();
    em.setcachemanagerconfigfile("classpath:ehcache-shiro.xml");
    return em;
  }
  @bean(name = "myshirocasrealm")
  public myshirocasrealm myshirocasrealm(ehcachemanager cachemanager) {
    myshirocasrealm realm = new myshirocasrealm();
    realm.setcachemanager(cachemanager);
    //realm.setcasserverurlprefix(shirocasconfiguration.casserverurlprefix);
    // 客户端回调地址
    //realm.setcasservice(shirocasconfiguration.shiroserverurlprefix + shirocasconfiguration.casfilterurlpattern);
    return realm;
  }
  /**
   * 注册单点登出listener
   * @return
   */
  @bean
  public servletlistenerregistrationbean singlesignouthttpsessionlistener(){
    servletlistenerregistrationbean bean = new servletlistenerregistrationbean();
    bean.setlistener(new singlesignouthttpsessionlistener());
//    bean.setname(""); //默认为bean name
    bean.setenabled(true);
    //bean.setorder(ordered.highest_precedence); //设置优先级
    return bean;
  }
  /**
   * 注册单点登出filter
   * @return
   */
  @bean
  public filterregistrationbean singlesignoutfilter(){
    filterregistrationbean bean = new filterregistrationbean();
    bean.setname("singlesignoutfilter");
    bean.setfilter(new singlesignoutfilter());
    bean.addurlpatterns("/*");
    bean.setenabled(true);
    //bean.setorder(ordered.highest_precedence);
    return bean;
  }
  /**
   * 注册delegatingfilterproxy(shiro)
   *
   * @return
   * @author shanhy
   * @create 2016年1月13日
   */
  @bean
  public filterregistrationbean delegatingfilterproxy() {
    filterregistrationbean filterregistration = new filterregistrationbean();
    filterregistration.setfilter(new delegatingfilterproxy("shirofilter"));
    // 该值缺省为false,表示生命周期由springapplicationcontext管理,设置为true则表示由servletcontainer管理
    filterregistration.addinitparameter("targetfilterlifecycle", "true");
    filterregistration.setenabled(true);
    filterregistration.addurlpatterns("/*");
    return filterregistration;
  }
  @bean(name = "lifecyclebeanpostprocessor")
  public lifecyclebeanpostprocessor getlifecyclebeanpostprocessor() {
    return new lifecyclebeanpostprocessor();
  }
  @bean
  public defaultadvisorautoproxycreator getdefaultadvisorautoproxycreator() {
    defaultadvisorautoproxycreator daap = new defaultadvisorautoproxycreator();
    daap.setproxytargetclass(true);
    return daap;
  }
  @bean(name = "securitymanager")
  public defaultwebsecuritymanager getdefaultwebsecuritymanager(myshirocasrealm myshirocasrealm) {
    defaultwebsecuritymanager dwsm = new defaultwebsecuritymanager();
    dwsm.setrealm(myshirocasrealm);
//   <!-- 用户授权/认证信息cache, 采用ehcache 缓存 -->
    dwsm.setcachemanager(getehcachemanager());
    // 指定 subjectfactory
    dwsm.setsubjectfactory(new cassubjectfactory());
    return dwsm;
  }
  @bean
  public authorizationattributesourceadvisor getauthorizationattributesourceadvisor(defaultwebsecuritymanager securitymanager) {
    authorizationattributesourceadvisor aasa = new authorizationattributesourceadvisor();
    aasa.setsecuritymanager(securitymanager);
    return aasa;
  }
  /**
   * cas过滤器
   *
   * @return
   * @author shanhy
   * @create 2016年1月17日
   */
  @bean(name = "casfilter")
  public casfilter getcasfilter() {
    casfilter casfilter = new casfilter();
    casfilter.setname("casfilter");
    casfilter.setenabled(true);
    // 登录失败后跳转的url,也就是 shiro 执行 casrealm 的 dogetauthenticationinfo 方法向casserver验证tiket
    casfilter.setfailureurl(loginurl);// 我们选择认证失败后再打开登录页面
    return casfilter;
  }
  /**
   * shirofilter<br/>
   * 注意这里参数中的 studentservice 和 iscoredao 只是一个例子,因为我们在这里可以用这样的方式获取到相关访问数据库的对象,
   * 然后读取数据库相关配置,配置到 shirofilterfactorybean 的访问规则中。实际项目中,请使用自己的service来处理业务逻辑。
   *
   * @param securitymanager
   * @param casfilter
   * @param userdao
   * @return
   * @author shanhy
   * @create 2016年1月14日
   */
  @bean(name = "shirofilter")
  public shirofilterfactorybean getshirofilterfactorybean(defaultwebsecuritymanager securitymanager, casfilter casfilter, userdao userdao) {
    shirofilterfactorybean shirofilterfactorybean = new shirofilterfactorybean();
    // 必须设置 securitymanager
    shirofilterfactorybean.setsecuritymanager(securitymanager);
    // 如果不设置默认会自动寻找web工程根目录下的"/login.jsp"页面
    shirofilterfactorybean.setloginurl(loginurl);
    // 登录成功后要跳转的连接
    shirofilterfactorybean.setsuccessurl(loginsuccessurl);
    shirofilterfactorybean.setunauthorizedurl(unauthorizedurl);
    // 添加casfilter到shirofilter中
    map<string, filter> filters = new hashmap<>();
    filters.put("casfilter", casfilter);
    // filters.put("logout",logoutfilter());
    shirofilterfactorybean.setfilters(filters);
    loadshirofilterchain(shirofilterfactorybean, userdao);
    return shirofilterfactorybean;
  }
  /**
   * 加载shirofilter权限控制规则(从数据库读取然后配置),角色/权限信息由myshirocasrealm对象提供dogetauthorizationinfo实现获取来的
   *
   * @author shanhy
   * @create 2016年1月14日
   */
  private void loadshirofilterchain(shirofilterfactorybean shirofilterfactorybean, userdao userdao){
    /////////////////////// 下面这些规则配置最好配置到配置文件中 ///////////////////////
    map<string, string> filterchaindefinitionmap = new linkedhashmap<string, string>();
    // authc:该过滤器下的页面必须登录后才能访问,它是shiro内置的一个拦截器org.apache.shiro.web.filter.authc.formauthenticationfilter
    // anon: 可以理解为不拦截
    // user: 登录了就不拦截
    // roles["admin"] 用户拥有admin角色
    // perms["permission1"] 用户拥有permission1权限
    // filter顺序按照定义顺序匹配,匹配到就验证,验证完毕结束。
    // url匹配通配符支持:? * **,分别表示匹配1个,匹配0-n个(不含子路径),匹配下级所有路径
    //1.shiro集成cas后,首先添加该规则
    filterchaindefinitionmap.put(casfilterurlpattern, "casfilter");
    //filterchaindefinitionmap.put("/logout","logout"); //logut请求采用logout filter
    //2.不拦截的请求
    filterchaindefinitionmap.put("/css/**","anon");
    filterchaindefinitionmap.put("/js/**","anon");
    filterchaindefinitionmap.put("/login", "anon");
    filterchaindefinitionmap.put("/logout","anon");
    filterchaindefinitionmap.put("/error","anon");
    //3.拦截的请求(从本地数据库获取或者从casserver获取(webservice,http等远程方式),看你的角色权限配置在哪里)
    filterchaindefinitionmap.put("/user", "authc"); //需要登录
    filterchaindefinitionmap.put("/user/add/**", "authc,roles[admin]"); //需要登录,且用户角色为admin
    filterchaindefinitionmap.put("/user/delete/**", "authc,perms[\"user:delete\"]"); //需要登录,且用户有权限为user:delete
    //4.登录过的不拦截
    filterchaindefinitionmap.put("/**", "user");
    shirofilterfactorybean.setfilterchaindefinitionmap(filterchaindefinitionmap);
  }
}
package com.hdwang.config.shirocas;
import javax.annotation.postconstruct;
import com.hdwang.dao.userdao;
import com.hdwang.entity.user;
import org.apache.shiro.securityutils;
import org.apache.shiro.authc.authenticationinfo;
import org.apache.shiro.authc.authenticationtoken;
import org.apache.shiro.authz.authorizationinfo;
import org.apache.shiro.authz.simpleauthorizationinfo;
import org.apache.shiro.cas.casrealm;
import org.apache.shiro.subject.principalcollection;
import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springframework.beans.factory.annotation.autowired;
import java.util.hashset;
import java.util.set;
/**
 * created by hdwang on 2017/6/20.
 * 安全数据源
 */
public class myshirocasrealm extends casrealm{
  private static final logger logger = loggerfactory.getlogger(myshirocasrealm.class);
  @autowired
  private userdao userdao;
  @postconstruct
  public void initproperty(){
//   setdefaultroles("role_user");
    setcasserverurlprefix(shirocasconfiguration.casserverurlprefix);
    // 客户端回调地址
    setcasservice(shirocasconfiguration.shiroserverurlprefix + shirocasconfiguration.casfilterurlpattern);
  }
//  /**
//   * 1、cas认证 ,验证用户身份
//   * 2、将用户基本信息设置到会话中(不用了,随时可以获取的)
//   */
//  @override
//  protected authenticationinfo dogetauthenticationinfo(authenticationtoken token) {
//
//    authenticationinfo authc = super.dogetauthenticationinfo(token);
//
//    string account = (string) authc.getprincipals().getprimaryprincipal();
//
//    user user = userdao.getbyname(account);
//    //将用户信息存入session中
//    securityutils.getsubject().getsession().setattribute("user", user);
//
//    return authc;
//  }
  /**
   * 权限认证,为当前登录的subject授予角色和权限
   * @see 经测试:本例中该方法的调用时机为需授权资源被访问时
   * @see 经测试:并且每次访问需授权资源时都会执行该方法中的逻辑,这表明本例中默认并未启用authorizationcache
   * @see 经测试:如果连续访问同一个url(比如刷新),该方法不会被重复调用,shiro有一个时间间隔(也就是cache时间,在ehcache-shiro.xml中配置),超过这个时间间隔再刷新页面,该方法会被执行
   */
  @override
  protected authorizationinfo dogetauthorizationinfo(principalcollection principalcollection) {
    logger.info("##################执行shiro权限认证##################");
    //获取当前登录输入的用户名,等价于(string) principalcollection.fromrealm(getname()).iterator().next();
    string loginname = (string)super.getavailableprincipal(principalcollection);
    //到数据库查是否有此对象(1.本地查询 2.可以远程查询casserver 3.可以由casserver带过来角色/权限其它信息)
    user user=userdao.getbyname(loginname);// 实际项目中,这里可以根据实际情况做缓存,如果不做,shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
    if(user!=null){
      //权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission)
      simpleauthorizationinfo info=new simpleauthorizationinfo();
      //给用户添加角色(让shiro去验证)
      set<string> rolenames = new hashset<>();
      if(user.getname().equals("boy5")){
        rolenames.add("admin");
      }
      info.setroles(rolenames);
      if(user.getname().equals("李四")){
        //给用户添加权限(让shiro去验证)
        info.addstringpermission("user:delete");
      }
      // 或者按下面这样添加
      //添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色
//      simpleauthorinfo.addrole("admin");
      //添加权限
//      simpleauthorinfo.addstringpermission("admin:manage");
//      logger.info("已为用户[mike]赋予了[admin]角色和[admin:manage]权限");
      return info;
    }
    // 返回null的话,就会导致任何用户访问被拦截的请求时,都会自动跳转到unauthorizedurl指定的地址
    return null;
  }
}
package com.hdwang.controller;
import com.hdwang.config.shirocas.shirocasconfiguration;
import com.hdwang.entity.user;
import org.springframework.stereotype.controller;
import org.springframework.ui.model;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.requestmethod;
import javax.servlet.http.httpsession;
/**
 * created by hdwang on 2017/6/21.
 * 跳转至cas server去登录(一个入口)
 */
@controller
@requestmapping("")
public class caslogincontroller {
  /**
   * 一般用不到
   * @param model
   * @return
   */
  @requestmapping(value="/login",method= requestmethod.get)
  public string loginform(model model){
    model.addattribute("user", new user());
//   return "login";
    return "redirect:" + shirocasconfiguration.loginurl;
  }
  @requestmapping(value = "logout", method = { requestmethod.get,
      requestmethod.post })
  public string loginout(httpsession session)
  {
    return "redirect:"+shirocasconfiguration.logouturl;
  }
}
package com.hdwang.controller;
import com.alibaba.fastjson.jsonobject;
import com.hdwang.entity.user;
import com.hdwang.service.datajpa.userservice;
import org.apache.shiro.securityutils;
import org.apache.shiro.mgt.securitymanager;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.controller;
import org.springframework.ui.modelmap;
import org.springframework.web.bind.annotation.requestmapping;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpsession;
/**
 * created by hdwang on 2017/6/19.
 */
@controller
@requestmapping("/home")
public class homecontroller {
  @autowired
  userservice userservice;
  @requestmapping("")
  public string index(httpsession session, modelmap map, httpservletrequest request){
//    user user = (user) session.getattribute("user");
    system.out.println(request.getuserprincipal().getname());
    system.out.println(securityutils.getsubject().getprincipal());
    user loginuser = userservice.getloginuser();
    system.out.println(jsonobject.tojsonstring(loginuser));
    map.put("user",loginuser);
    return "home";
  }
}

 4.运行验证

登录

访问:

跳转至:

输入正确用户名密码登录跳转回:

最终跳回:

登出

访问:

跳转至:

由于未登录,又执行登录步骤,所以最终返回

这次登录成功后返回:

cas server端登出(也行)

访问:

再访问: 会跳转至登录页,perfect!

以上所述是小编给大家介绍的spring boot 1.5.4 集成shiro+cas,实现单点登录和权限控制,希望对大家有所帮助

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网