zoukankan      html  css  js  c++  java
  • springboot+shiro+cas实现单点登录之shiro端搭建

    github:https://github.com/peterowang/shiro-cas    

    本文如有配置问题,请查看之前的springboot集成shiro的文章

    1.配置ehcache缓存,在resource下创建config,再创建ehcache-shiro.xml添加:

    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache updateCheck="false" name="shiroCache">
    
        <defaultCache
                maxElementsInMemory="10000"
                eternal="false"
                timeToIdleSeconds="120"
                timeToLiveSeconds="120"
                overflowToDisk="false"
                diskPersistent="false"
                diskExpiryThreadIntervalSeconds="120"
        />
    </ehcache>

    2.添加maven依赖
    <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-cas</artifactId>
                <version>1.2.4</version>
            </dependency>   
    3.配置shiro+cas
    添加
    MyShiroCasRealm 类
    package com.example.demo.config.shiro;

    import com.example.demo.model.UserInfo;
    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 javax.annotation.PostConstruct;

    /**
    * Created by BFD-593 on 2017/8/11.
    */
    public class MyShiroCasRealm extends CasRealm {
    private static final Logger logger = LoggerFactory.getLogger(MyShiroCasRealm.class);
    @PostConstruct
    public void initProperty(){
    // cas地址
    setCasServerUrlPrefix(ShiroConfiguration.casServerUrlPrefix);
    // 客户端回调地址
    setCasService(ShiroConfiguration.shiroServerUrlPrefix + ShiroConfiguration.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;
    // }

    /**
    * 此方法调用 hasRole,hasPermission的时候才会进行回调.
    *
    * 权限信息.(授权): 1、如果用户正常退出,缓存自动清空; 2、如果用户非正常退出,缓存自动清空;
    * 3、如果我们修改了用户的权限,而用户不退出系统,修改的权限无法立即生效。 (需要手动编程进行实现;放在service进行调用)
    * 在权限修改后调用realm中的方法,realm已经由spring管理,所以从spring中获取realm实例, 调用clearCached方法;
    * :Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
    *
    * @param principals
    * @return
    */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    logger.info("开始权限配置");
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    // UserInfo userInfo = (UserInfo)principals.getPrimaryPrincipal();
    //这里应该查询数据库,拿到用户的所有角色,遍历添加角色到权限对象中,再通过角色获取权限,添加到权限对象中
    /* for (Role role: userInfo.getRoleList()) {
    authorizationInfo.addRole(role.getRole());
    for (SysPermission p: role.getPermissions()) {
    authorizationInfo.addStringPermission(p.getPermission());
    }
    }*/
    //为了节省时间,这边我先给它写死,做测试
    authorizationInfo.addRole("wangjing");
    authorizationInfo.addStringPermission("userinfo:view");
    return authorizationInfo;
    }
    }




    添加shiro配置类,主要是将casFilter添加到shiroFilter中
    package com.example.demo.config.shiro;

    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.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.web.filter.DelegatingFilterProxy;
    import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

    import javax.servlet.Filter;
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.Map;
    import java.util.Properties;

    /**
    * shiro配置类
    * Created by BFD-593 on 2017/8/8.
    */
    @Configuration
    public class ShiroConfiguration {
    private static final Logger logger = LoggerFactory.getLogger(ShiroConfiguration.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:8089";
    // casFilter UrlPattern
    public static final String casFilterUrlPattern = "/cas";
    // 登录地址
    public static final String loginUrl = casLoginUrl + "?service=" + shiroServerUrlPrefix + casFilterUrlPattern;
    // 登出地址(casserver启用service跳转功能,需在webappscasWEB-INFcas.properties文件中启用cas.logout.followServiceRedirects=true)
    public static final String logoutUrl = casLogoutUrl+"?service="+shiroServerUrlPrefix;
    // 登录成功地址
    public static final String loginSuccessUrl = "/index";
    // 权限认证失败跳转地址
    public static final String unauthorizedUrl = "/403";
    @Bean
    public EhCacheManager getEhCacheManager() {
    EhCacheManager em = new EhCacheManager();
    em.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
    return em;
    }

    @Bean(name = "myShiroCasRealm")
    public MyShiroCasRealm myShiroCasRealm(EhCacheManager cacheManager) {
    MyShiroCasRealm realm = new MyShiroCasRealm();
    realm.setCacheManager(cacheManager);
    return realm;
    }

    /**
    * 注册单点登出listener
    * @return
    */
    @Bean
    public ServletListenerRegistrationBean singleSignOutHttpSessionListener(){
    ServletListenerRegistrationBean bean = new ServletListenerRegistrationBean();
    bean.setListener(new SingleSignOutHttpSessionListener());
    bean.setEnabled(true);
    return bean;
    }

    /**
    * 注册单点登出filter
    * @return
    */
    @Bean
    public FilterRegistrationBean singleSignOutFilter(){
    FilterRegistrationBean bean = new FilterRegistrationBean();
    bean.setName("singleSignOutFilter");
    bean.setFilter(new SingleSignOutFilter());
    bean.addUrlPatterns("/*");
    bean.setEnabled(true);
    return bean;
    }



    /**
    * 注册DelegatingFilterProxy(Shiro)
    */
    @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(@Qualifier("myShiroCasRealm") MyShiroCasRealm myShiroCasRealm) {
    DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
    dwsm.setRealm(myShiroCasRealm);
    //用户授权/认证信息Cache, 采用EhCache 缓存
    dwsm.setCacheManager(getEhCacheManager());
    // 指定 SubjectFactory
    dwsm.setSubjectFactory(new CasSubjectFactory());
    return dwsm;
    }



    /**
    * CAS过滤器
    *
    * @return
    */
    @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
    * @param securityManager
    * @param casFilter
    * @return
    */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager,
    @Qualifier("casFilter") CasFilter casFilter) {
    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);
    shiroFilterFactoryBean.setFilters(filters);

    loadShiroFilterChain(shiroFilterFactoryBean);
    return shiroFilterFactoryBean;
    }

    /**
    * 加载shiroFilter权限控制规则(从数据库读取然后配置),角色/权限信息由MyShiroCasRealm对象提供doGetAuthorizationInfo实现获取来的
    */
    private void loadShiroFilterChain(@Qualifier("shiroFilter") ShiroFilterFactoryBean shiroFilterFactoryBean){
    /////////////////////// 下面这些规则配置最好配置到配置文件中 ///////////////////////
    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");

    //2.不拦截的请求
    filterChainDefinitionMap.put("/css/**","anon");
    filterChainDefinitionMap.put("/login", "anon");
    filterChainDefinitionMap.put("/logout","anon");
    filterChainDefinitionMap.put("/error","anon");

    //3.拦截的请求,并且拥有哪些权限才可以访问,当没有权限时
    // 我们通过在shiroFilter里设置 shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
    // 让其跳转到403页面,需要加403的controller请求哦
    // 这里还可以通过另一种方式,就是在controller的需要的请求上,加shiro的权限注解
    // 如果通过注解的方式,则需要通过以下两个配置bean来分别设置支持shiro注解和无权限跳转的页面
    filterChainDefinitionMap.put("/userinfo/userList", "authc,perms["userinfo:view"],roles["wangjing"]"); //需要登录,且用户有权限为userinfo:view并且角色为wangjing
    filterChainDefinitionMap.put("/userinfo/userDel", "authc,perms["userinfo:view"],roles["admin"]");
    filterChainDefinitionMap.put("/userinfo/userAdd", "authc,perms["userinfo:view"],roles["redhat"]");
    //4.登录过的不拦截
    filterChainDefinitionMap.put("/**", "user");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    }
    /*
    *//**
    * 权限认证
    * 需要开启Shiro AOP注解支持
    * @RequiresPermissions({"userinfo:view"})
    * @RequiresRoles({"wangjing"})等注解的支持
    * @param securityManager
    * @return
    *//*
    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
    aasa.setSecurityManager(securityManager);
    return aasa;
    }*/
    /**
    * 当用户无权限访问403页面而不抛异常,默认shiro会报UnauthorizedException异常
    * @return
    */
    /* @Bean
    public SimpleMappingExceptionResolver resolver() {
    SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
    Properties properties = new Properties();
    properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/403");
    resolver.setExceptionMappings(properties);
    return resolver;
    }*/
    }

    添加HomeController
    package com.example.demo.web;

    import com.example.demo.config.shiro.ShiroConfiguration;
    import com.example.demo.model.UserInfo;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    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 BFD-593 on 2017/8/8.
    */
    @Controller
    public class HomeController {
    private static final Logger log = LoggerFactory.getLogger(HomeController.class);
    @RequestMapping({"/","/index"})
    public String index() {
    return "index";
    }

    /**
    * shiroFilterFactoryBean.setLoginUrl(loginUrl);我们设置了登录地址,但没设置logout,
    * 所以加一个logout的请求,转到cas的logout上
    * @param session
    * @return
    */
    @RequestMapping(value = "logout", method = { RequestMethod.GET,
    RequestMethod.POST })
    public String loginout(HttpSession session)
    {
    return "redirect:"+ShiroConfiguration.logoutUrl;
    }
    @RequestMapping("/403")
    public String fail(){
    return "403";
    }
    }

    添加UserController
    package com.example.demo.web;

    import org.apache.shiro.authz.annotation.RequiresAuthentication;
    import org.apache.shiro.authz.annotation.RequiresPermissions;
    import org.apache.shiro.authz.annotation.RequiresRoles;
    import org.apache.shiro.authz.annotation.RequiresUser;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;

    /**
    * Created by BFD-593 on 2017/8/9.
    */
    @Controller
    @RequestMapping("/userinfo")
    public class UserController {
    /**
    * 要查看必须有角色wangjing和有权限userinfo:view
    * @return
    */
    @RequestMapping("/userList")
    public String userInfo(){
    return "userInfo";
    }

    /**
    * 用户添加必须有查看和删除权限;
    * @return
    */
    @RequestMapping("/userAdd")
    public String userInfoAdd(){
    return "userAdd";
    }

    /**
    * 要删除必须有查看和删除权限
    * @return
    */
    @RequestMapping("/userDel")
    public String userInfoDel() {
    return "userDel";
    }

    }

    运行验证

    登录 

    访问:http://localhost:8089

    跳转至:https://localhost:8443/cas/login?service=http://localhost:8089/cas

    输入正确用户名密码登录跳转回:http://localhost:8089/cas?ticket=ST-203-GUheN64mOZec9IWZSH1B-cas01.example.org

    最终跳回:http://localhost:8089/index

    登出

    访问:http://localhost:8089/logout

    跳转至:https://localhost:8443/cas/logout?service=http://localhost:8089/cas

    这次登录成功后返回:http://localhost:8089/index

  • 相关阅读:
    把git项目放到个人服务器上
    关于fcitx无法切换输入法的问题解决
    博客变迁通知
    (欧拉回路 并查集 别犯傻逼的错了) 7:欧拉回路 OpenJudge 数据结构与算法MOOC / 第七章 图 练习题(Excercise for chapter7 graphs)
    (并查集) HDU 1856 More is better
    (并查集 不太会) HDU 1272 小希的迷宫
    (并查集 注意别再犯傻逼的错了) HDU 1213 How Many Tables
    (最小生成树 Kruskal算法) 51nod 1212 无向图最小生成树
    (并查集) HDU 1232 畅通工程
    (最小生成树 Prim) HDU 1233 还是畅通工程
  • 原文地址:https://www.cnblogs.com/wangjing666/p/7347077.html
Copyright © 2011-2022 走看看