学习

spring-boot客户端集成shiro-cas实现单点登录解

2021-10-07  本文已影响0人  清远_03d9

1. spring-boot客户端集成shiro-cas实现单点登录解决方案

1. maven依赖引入

<!--shiro start-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-cas</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.4.0</version>
        </dependency>
<!--shiro end-->

2. application.properties配置

# http://localhost:9000/sso/auth:sa-token服务的统一认证地址,http://127.0.0.1:8082/jdtbsh/sso/cas为客户端的重定向地址
shiro.loginUrl=http://localhost:9000/sso/auth?redirect=http://127.0.0.1:8082/jdtbsh/sso/cas
# sa-token服务的登出接口
shiro.logoutUrl=http://localhost:9000/sso/logout
# 登陆成功后客户端跳转页面
shiro.successUrl=/main/index
shiro.failureUrl
# sa-token服务地址前缀
shiro.cas.serverUrlPrefix=http://localhost:9000
# 客户端应用的CAS服务URL,用于接收和处理CAS服务端的Ticket
shiro.cas.service=http://127.0.0.1:8082/jdtbsh/main/index
# sa-token秘钥
sa-token.secretkey=DIITabc123

3. ShiroConfiguration自定义bean

@Configuration
public class ShiroConfiguration {

    /**
     *成功跳转地址
     */
    @Value("${shiro.successUrl}")
    private String successUrl;
    /**
     * 失败跳转地址
     */
    @Value("${shiro.failureUrl}")
    private String failureUrl;
    /**
     * 登录地址
     */
    @Value("${shiro.loginUrl}")
    private String loginUrl;

    /**
     * 登出地址
     */
    @Value("${shiro.logoutUrl}")
    private String logoutUrl;

    /**
     * 回调地址
     */
    @Value("${shiro.cas.service}")
    private String casService;

    /**
     * CAS 地址
     */
    @Value("${shiro.cas.serverUrlPrefix}")
    private String casServerUrlPrefix;


    @Value("${lxgz.dlfs}")
    private String dlfs;

    @Bean
    public CasFilter casFilter(){
        return new CasFilter();
    }

    /**
     * ShiroFilterFactoryBean 处理拦截资源文件问题。
     * 注意:单独一个ShiroFilterFactoryBean配置是或报错的
     * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //配置自定义casFilter
        Map<String, Filter> filters = new LinkedHashMap<>();
        CasFilter casFilter = new CasFilter();
        casFilter.setFailureUrl(failureUrl);
        casFilter.setSuccessUrl(successUrl);
        filters.put("cas", casFilter);
        LogoutFilter logoutFilter = new LogoutFilter();
        logoutFilter.setRedirectUrl(logoutUrl);
        filters.put("logout", logoutFilter);
        shiroFilterFactoryBean.setFilters(filters);
        //未授权跳转页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        //service指定了登录成功后的回调地址,回调/cas将被CasFilter拦截,获取服务端返回的Service Ticket进行登录
        shiroFilterFactoryBean.setLoginUrl(loginUrl);
        //登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl(successUrl);
        // 拦截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //拦截CAS Server返回的ticket
        filterChainDefinitionMap.put("/sso/cas", "cas");
        //退出登录
        //filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/login_sso", "anon");
        filterChainDefinitionMap.put("/**", "authc");
        filterChainDefinitionMap.put("/", "authc");
        // 未授权界面;
        //shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 自定义Realm
     * @return
     */
    @Bean
    public ShiroRealm casRealm(){
        ShiroRealm casRealm = new ShiroRealm();
        casRealm.setCachingEnabled(true);
        casRealm.setAuthenticationCachingEnabled(true);
        casRealm.setAuthenticationCacheName("authenticationCache");
        casRealm.setAuthorizationCachingEnabled(true);
        casRealm.setAuthorizationCacheName("authorizationCache");
        //指定CAS服务端地址
        casRealm.setCasServerUrlPrefix(casServerUrlPrefix);
        //当前应用的CAS服务URL,用于接收和处理CAS服务端的Ticket
        casRealm.setCasService(casService);
        return casRealm;
    }

    @Bean
    public SecurityManager securityManager(ShiroRealm casRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //ShiroRealm myRealm = new ShiroRealm();
        securityManager.setRealm(casRealm);
        securityManager.setSessionManager(sessionManager());
        // securityManager.setCacheManager(ehCacheManager());
        return securityManager;
    }

    @Bean
    public SessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        // 设置session过期时间3600s
        sessionManager.setGlobalSessionTimeout(36000000L);
        return sessionManager;
    }
}

4. 自定义Realm

public class ShiroRealm extends CasRealm {

    
    /*
     * 登录信息和用户验证信息验证(non-Javadoc)
     * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String)token.getPrincipal();
        CasToken casToken = (CasToken) token;
        if (token == null) {
            return null;
        }
        String ticket = (String)casToken.getCredentials();
        if (!StringUtils.hasText(ticket)) {
            return null;
        }
        //由sso服务器验证ticket的合法性,获取登录信息补全
        JSONObject loginInfo = loginByTicket(ticket);
        //token值
        String tokenVal = loginInfo.getString("data");
        //登录账号
        String loginId = loginInfo.getString("loginId");
        Assertion casAssertion =  new AssertionImpl(loginId);;
        // 从CAS服务端中获取相关属性,包括用户名、是否设置RememberMe等
        AttributePrincipal casPrincipal = casAssertion.getPrincipal();
        String userId = casPrincipal.getName();
        casToken.setUserId(userId);
        Map<String, Object> attributes = casPrincipal.getAttributes();
        String rememberMeAttributeName = getRememberMeAttributeName();
        String rememberMeStringValue = (String)attributes.get(rememberMeAttributeName);
        boolean isRemembered = rememberMeStringValue != null && Boolean.parseBoolean(rememberMeStringValue);
        if (isRemembered) {
            casToken.setRememberMe(true);
        }
        //根据业务需要将用户信息存储至session中
        updateSession(loginId);
        List<Object> principals = CollectionUtils.asList(userId, attributes);
        PrincipalCollection principalCollection = new SimplePrincipalCollection(principals, getName());
        return new SimpleAuthenticationInfo(principalCollection, ticket);
    }

    /*
     * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法(non-Javadoc)
     * @see org.apache.shiro.realm.AuthorizingRealm#doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
        return null;
    }

    private JSONObject loginByTicket(String ticket){
        try
        {
            String ssoServerUrl = getCasServerUrlPrefix();
            String tokenStr = HttpUtil
                    .createGet(ssoServerUrl + "/sso/doLoginByTicket" +"?ticket="+ticket)
                    .execute()
                    .body();
            JSONObject jsonObject = JSON.parseObject(tokenStr);
            String loginId = jsonObject.getString("loginId");
            return jsonObject;
        }catch (Exception ex){
            ex.printStackTrace();
        }
        return null;
    }
    //将用户信息存储至session,根据业务需要而定,此方法可忽略
    private void updateSession(String loginId){
    }
}

5. 登出接口

   /**
     * 用户注销
     *
     * @param
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/logout")
    public ModelAndView logout() throws Exception {
            //获取当前登录用户名
            String USERNAME = Jurisdiction.getUsername();    //当前登录的用户名
            String tokenUrl = Const.WEB_APP_CONTEXT.getEnvironment().getProperty("shiro.loginUrl");
            String secretkey = Const.WEB_APP_CONTEXT.getEnvironment().getProperty("sa-token.secretkey");
            String logoutUrl = Const.WEB_APP_CONTEXT.getEnvironment().getProperty("shiro.logoutUrl");
            //调用sa-token服务登出接口
            String loginIdStr = HttpUtil
                    .createGet(logoutUrl +"?" + "loginId=" + USERNAME + "&secretkey=" + secretkey)
                    .execute()
                    .body();
            if("ok".equals(loginIdStr)) {
                mv.setView(new RedirectView(tokenUrl));
            }
        mv.addObject("pd", pd);
        return mv;
    }
上一篇下一篇

猜你喜欢

热点阅读