springboot+cas5.x+shiro+pac4j实现sso集成客户端(四)

按照上篇的cas服务端的配置一样,集成shiro+pac4j,其实在1.3还是1.2shiro-cas就过期了,shiro官方也让我们集成pac4j,但是居然居然没有demo,我也是服,,。。

此篇博客将集成springboot+cas5+shiro+pac4j 集成在一起,是宝宝将近一个月的心血,还有老大的帮忙!!!!

开始我的表演:

首先是pom



    4.0.0

    com.bofeng.shiro
    shiro-cas-pac4j
    1.0.0


    
        1.8
        UTF-8
        1.8
        1.1.3
        3.4
        18.0
        1.2.8
        2.4
        1.10
        1.4.0
        3.0.0
        2.1.0
        3.4.1
        0.2.7
        1.7.21
        1.1.7
        1.16.20

    

    
        
            org.apache.shiro
            shiro-spring-boot-web-starter
            ${shiro.version}
        
        
            io.buji
            buji-pac4j
            ${buji.version}
        
        
            org.pac4j
            pac4j-cas
            ${pac4j.version}
        
        
            org.pac4j
            pac4j-jwt
            ${pac4j.version}
        
        
            org.pac4j
            pac4j-http
            ${pac4j.version}
        
        
            com.alibaba
            fastjson
            1.2.7
        
        
            org.slf4j
            slf4j-api
            ${slf4j.version}
        
        
            ch.qos.logback
            logback-classic
            ${logback.version}
        
        
            ch.qos.logback
            logback-core
            ${logback.version}
        
        
            org.projectlombok
            lombok
            ${lombok.version}
            provided
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    true
                
            
        
    

然后是shiro 的认证,由于集成了pac4j,所以我们只要关心权限

的控制就可以了

@Slf4j
public class ShiroPac4jRealm extends Pac4jRealm {

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(final PrincipalCollection principals) {
        Object user = super.getAvailablePrincipal(principals);
        log.info("登录用户:{}", user);
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        List permissions = new ArrayList();
        permissions.add("user:info");
        simpleAuthorizationInfo.addStringPermissions(permissions);

        return simpleAuthorizationInfo;
    }

}

shiro这里不详解,只说集成,shiro的配置如下

 

package com.bofeng.shiro.config;

import io.buji.pac4j.filter.CallbackFilter;
import io.buji.pac4j.filter.SecurityFilter;
import io.buji.pac4j.subject.Pac4jSubjectFactory;
import org.apache.shiro.mgt.SubjectFactory;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.AbstractShiroWebFilterConfiguration;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.pac4j.cas.client.CasClient;
import org.pac4j.cas.client.rest.CasRestFormClient;
import org.pac4j.cas.config.CasConfiguration;
import org.pac4j.cas.config.CasProtocol;
import org.pac4j.core.client.Clients;
import org.pac4j.core.config.Config;
import org.pac4j.core.matching.PathMatcher;
import org.pac4j.http.client.direct.ParameterClient;
import org.pac4j.jwt.config.encryption.SecretEncryptionConfiguration;
import org.pac4j.jwt.config.signature.SecretSignatureConfiguration;
import org.pac4j.jwt.credentials.authenticator.JwtAuthenticator;
import org.pac4j.jwt.profile.JwtGenerator;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
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.context.annotation.DependsOn;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.DelegatingFilterProxy;

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

@Configuration
public class ShiroConfiguration extends AbstractShiroWebFilterConfiguration {

    @Value("#{ @environment['cas.prefixUrl'] ?: null }")
    private String prefixUrl;

    @Value("#{ @environment['cas.loginUrl'] ?: null }")
    private String casLoginUrl;

    @Value("#{ @environment['cas.callbackUrl'] ?: null }")
    private String callbackUrl;

    //jwt秘钥
    @Value("${jwt.salt}")
    private String salt;


    /**
     * JWT Token 生成器,对CommonProfile生成然后每次携带token访问
     *
     * @return
     */
    @SuppressWarnings("rawtypes")
    @Bean
    protected JwtGenerator jwtGenerator() {
        return new JwtGenerator(new SecretSignatureConfiguration(salt), new SecretEncryptionConfiguration(salt));
    }

    @Bean
    protected JwtAuthenticator jwtAuthenticator() {
        JwtAuthenticator jwtAuthenticator = new JwtAuthenticator();
        jwtAuthenticator.addSignatureConfiguration(new SecretSignatureConfiguration(salt));
        jwtAuthenticator.addEncryptionConfiguration(new SecretEncryptionConfiguration(salt));
        return jwtAuthenticator;
    }

    /**
     * cas的基本设置,包括或url等等,rest调用协议等
     *
     * @return
     */
    @Bean
    public CasConfiguration casConfiguration() {
        CasConfiguration casConfiguration = new CasConfiguration(casLoginUrl);
        casConfiguration.setProtocol(CasProtocol.CAS20);
        casConfiguration.setPrefixUrl(prefixUrl);
        return casConfiguration;
    }

    /**
     * 不拦截的路径
     *
     * @return
     */
    @Bean
    public PathMatcher pathMatcher() {
        PathMatcher pathMatcher = new PathMatcher();
        pathMatcher.excludePath("/html/**");
        return pathMatcher;
    }

    /**
     * pac4jRealm
     *
     * @return
     */
    @Bean(name = "pac4jRealm")
    public Realm pac4jRealm() {
        return new ShiroPac4jRealm();
    }

    /**
     * 通过rest接口可以获取tgt,获取service ticket,甚至可以获取CasProfile
     *
     * @return
     */
    @Bean
    protected CasRestFormClient casRestFormClient(CasConfiguration casConfiguration) {
        CasRestFormClient casRestFormClient = new CasRestFormClient();
        casRestFormClient.setConfiguration(casConfiguration);
        casRestFormClient.setName("rest");
        return casRestFormClient;
    }

    /**
     * casClient
     *
     * @return
     */
    @Bean
    public CasClient casClient(CasConfiguration casConfiguration) {
        CasClient casClient = new CasClient();
        casClient.setConfiguration(casConfiguration);
        casClient.setCallbackUrl(callbackUrl);
        casClient.setName("cas");
        return casClient;
    }

    /**
     * token校验相关
     *
     * @return
     */
    @Bean
    protected Clients clients(CasClient casClient, CasRestFormClient casRestFormClient) {
        //可以设置默认client
        Clients clients = new Clients();
        //token校验器,可以用HeaderClient更安全
        ParameterClient parameterClient = new ParameterClient("token", jwtAuthenticator());
        parameterClient.setSupportGetRequest(true);
        parameterClient.setName("jwt");
        //支持的client全部设置进去
        clients.setClients(casClient, casRestFormClient, parameterClient);
        return clients;
    }

    @Bean
    protected Config casConfig(Clients clients) {
        Config config = new Config();
        config.setClients(clients);
        return config;
    }

    /**
     * 由于cas代理了用户,所以必须通过cas进行创建对象
     *
     * @return
     */
    @Bean(name = "subjectFactory")
    protected SubjectFactory subjectFactory() {
        return new Pac4jSubjectFactory();
    }

    /**
     * 单点登出的listener
     *
     * @return
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    @Bean
    public ServletListenerRegistrationBean singleSignOutHttpSessionListener() {
        ServletListenerRegistrationBean bean = new ServletListenerRegistrationBean();
        bean.setListener(new SingleSignOutHttpSessionListener());
        bean.setEnabled(true);
        return bean;
    }

    /**
     * 单点登出filter
     *
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public FilterRegistrationBean singleSignOutFilter() {
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setName("singleSignOutFilter");
        SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
        singleSignOutFilter.setCasServerUrlPrefix(prefixUrl);
        singleSignOutFilter.setIgnoreInitConfiguration(true);
        bean.setFilter(singleSignOutFilter);
        bean.addUrlPatterns("/*");
        bean.setEnabled(true);
        return bean;
    }

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager(Realm pac4jRealm, SubjectFactory subjectFactory) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(pac4jRealm);
        defaultWebSecurityManager.setSubjectFactory(subjectFactory);
        return defaultWebSecurityManager;
    }

    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new DelegatingFilterProxy("shiroFilter"));
        filterRegistrationBean.addInitParameter("targetFilterLifecycle", "true");
        filterRegistrationBean.setEnabled(true);
        filterRegistrationBean.addUrlPatterns("/*");
        return filterRegistrationBean;
    }

    @Bean(name = "shiroFilter")
    protected ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager, Config config) {
        ShiroFilterFactoryBean filterFactoryBean = super.shiroFilterFactoryBean();
        filterFactoryBean.setSecurityManager(securityManager);

        //过滤器设置
        Map filters = new HashMap();
        SecurityFilter securityFilter = new SecurityFilter();
        securityFilter.setClients("cas,rest,jwt");
        securityFilter.setConfig(config);
        filters.put("casSecurityFilter", securityFilter);

        CallbackFilter callbackFilter = new CallbackFilter();
        callbackFilter.setConfig(config);
        filters.put("callbackFilter", callbackFilter);

        filterFactoryBean.setFilters(filters);


        return filterFactoryBean;
    }

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
        definition.addPathDefinition("/callback", "callbackFilter");
        definition.addPathDefinition("/you", "anon");
        definition.addPathDefinition("/**", "casSecurityFilter");

        return definition;
    }

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     *
     * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 开启 shiro aop注解支持
     *
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
        aasa.setSecurityManager(securityManager);
        return aasa;
    }
}

一个配置类

package com.bofeng.shiro.config;

import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

@RestControllerAdvice
public class ShiroExceptionHandler {

//    @ExceptionHandler(value = {AuthorizationException.class})
//    public Map authorizationExceptionHandler(HttpServletRequest request, Exception e) {
//        return noPermissionResult();
//    }

    @ExceptionHandler(value = {UnauthorizedException.class})
    public Map unauthorizedExceptionHandler(HttpServletRequest request, Exception e) {
        return noPermissionResult();
    }

    private Map noPermissionResult() {
        Map result = new HashMap() {
            private static final long serialVersionUID = 1L;

            {
                put("errcode", 0);
                put("errmsg", "暂无权限");
            }
        };
        return result;
    }

}

启动类顺便也贴出来了

package com.bofeng.shiro;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @RequiresPermissions(value = "role:edit")
    @GetMapping(value = "/roles/{id}")
    public String put(){
        return "允许修改角色";
    }

    @RequiresPermissions(value = "user:info")
    @GetMapping(value = "/users/{id}")
    public PrincipalCollection getUserById() {
        return SecurityUtils.getSubject().getPrincipals();
    }

    @GetMapping(value = "/you")
    public String you(){
        return "you you 不需要权限";
    }

}

最重要的yml文件

server: 
  port: 8083

spring: 
  http: 
    encoding: 
    charset: UTF-8
    enabled: true
    force: true
    mail: 
        default-encoding: UTF-8
    messages: 
        encoding: UTF-8

casServerUrlPrefix: https://passport.sso.com:9898/cas
cas: 
  prefixUrl: https://passport.sso.com:9898/cas
  loginUrl: ${cas.prefixUrl}/login
  logoutUrl: ${cas.prefixUrl}/logout
  serviceUrl: http://www.casclient2.com:${server.port}
  callbackUrl: ${cas.serviceUrl}/callback
jwt:
  salt: cas

debug: true

 

配置什么的你们看自己需求改,host别说不会

到这里Springboot的客户端就集成好了,如果还是有问题的话Q845896876交流

如果这篇博客帮你解决了问题,那么那么请给宝宝一点动力

springboot+cas5.x+shiro+pac4j实现sso集成客户端(四)_第1张图片

 

 

源码提供给大家参考   请分开tomcat部署   https://download.csdn.net/download/weixin_39819191/10422417

验证码,自定义登录页面>https://blog.csdn.net/u010588262/article/details/80014083

springboot+cas5.x+shiro+pac4j实现sso集成客户端(四)_第2张图片

 

跑源码发现的几个类,springboot+cas5.x+shiro+pac4j实现sso集成客户端(四)_第3张图片

这个是前后端分离AJAX调用时返回的类,但是发现个没效果的问题,

最后让前端在请求头里面加入 X-Requested-With=XMLHttpRequest  就好了,不知道怎么回事,,,

springboot+cas5.x+shiro+pac4j实现sso集成客户端(四)_第4张图片

判断是不是ajax的类

问题: ajax请求后无法正常登陆,确认是session不对,跨域问题,

解决:前端判断401,跳转到后台一个特殊接口,接口无权访问会跳到cas登录页,登陆后跳转到接口,接口转发到前端路由

有点绕,,,但是就这么解决问题了,,

你可能感兴趣的:(cas)