美文网首页SpringBoot极简教程 · Spring Boot Java编程单点登录
CAS 5.3.1系列之自定义Shiro认证策略(四)

CAS 5.3.1系列之自定义Shiro认证策略(四)

作者: smileNicky | 来源:发表于2020-04-29 11:39 被阅读0次

    CAS 5.3.1系列之自定义Shiro认证策略(四)

    CAS官方文档是介绍基于配置实现shiro认证的,可以参考官方文档,不过我们也可以通过自定义认证策略的方式实现jdbc认证,pom先加入相关jar

    <!-- Custom Authentication -->
            <dependency>
                <groupId>org.apereo.cas</groupId>
                <artifactId>cas-server-core-authentication-api</artifactId>
                <version>${cas.version}</version>
            </dependency>
    
            <!-- Custom Configuration -->
            <dependency>
                <groupId>org.apereo.cas</groupId>
                <artifactId>cas-server-core-configuration-api</artifactId>
                <version>${cas.version}</version>
            </dependency>
    
            <dependency>
                <groupId>org.apereo.cas</groupId>
                <artifactId>cas-server-support-generic</artifactId>
                <version>${cas.version}</version>
            </dependency>
    

    如果要用公网提供的基于配置实现的,需要加入:

    <!-- Shiro Authentication -->
            <dependency>
                <groupId>org.apereo.cas</groupId>
                <artifactId>cas-server-support-shiro-authentication</artifactId>
                <version>${cas.version}</version>
            </dependency>
    

    要自定义shiroRealm的,加入shiro相关jar:

    <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.4.0</version>
            </dependency>
    

    实现一个Shiro Realm类,仅供参考:

    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.subject.Subject;
    import org.muses.jeeplatform.cas.user.model.User;
    import org.muses.jeeplatform.cas.user.service.UserService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.datasource.DriverManagerDataSource;
    
    /**
     * <pre>
     *
     * </pre>
     *
     * <pre>
     * @author mazq
     * 修改记录
     *    修改后版本:     修改人:  修改日期: 2020/04/26 11:33  修改内容:
     * </pre>
     */
    public class ShiroAuthorizingRealm extends AuthorizingRealm {
    
        Logger LOG = LoggerFactory.getLogger(ShiroAuthorizingRealm.class);
    
        /**注解引入业务类**/
        //@Autowired
        //UserService userService;
    
        /**
         * 登录信息和用户验证信息验证(non-Javadoc)
         * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(AuthenticationToken)
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
            String username = (String)token.getPrincipal();                 //得到用户名
            String password = new String((char[])token.getCredentials());   //得到密码
    
            LOG.info("Shiro doGetAuthenticationInfo>> username:{},password:{}",username,password);
    
            //User user = userService.findByUsername(username);
            // JDBC模板依赖于连接池来获得数据的连接,所以必须先要构造连接池
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://192.168.0.152:33306/jeeplatform");
            dataSource.setUsername("root");
            dataSource.setPassword("minstone");
    
            // 创建JDBC模板
            JdbcTemplate jdbcTemplate = new JdbcTemplate();
            jdbcTemplate.setDataSource(dataSource);
    
            String sql = "SELECT * FROM sys_user WHERE username = ?";
    
            User user = (User) jdbcTemplate.queryForObject(sql, new Object[]{username}, new BeanPropertyRowMapper(User.class));
            Subject subject = getCurrentExecutingSubject();
            //获取Shiro管理的Session
            Session session = getShiroSession(subject);
            //Shiro添加会话
            session.setAttribute("username", username);
            session.setAttribute(ShiroConsts.SESSION_USER, user);
    
            /**检测是否有此用户 **/
            if(user == null){
                throw new UnknownAccountException();//没有找到账号异常
            }
            /**检验账号是否被锁定 **/
            if(Boolean.TRUE.equals(user.getLocked())){
                throw new LockedAccountException();//抛出账号锁定异常
            }
            /**AuthenticatingRealm使用CredentialsMatcher进行密码匹配**/
            if(null != username && null != password){
                return new SimpleAuthenticationInfo(username, password, getName());
            }else{
                return null;
            }
    
        }
    
        /**
         * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法(non-Javadoc)
         * @see AuthorizingRealm#doGetAuthorizationInfo(PrincipalCollection)
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
            String username = (String)pc.getPrimaryPrincipal();
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    //        authorizationInfo.setRoles(userService.getRoles(username));
    //        authorizationInfo.setStringPermissions(userService.getPermissions(username));
            System.out.println("Shiro授权");
            return authorizationInfo;
        }
    
        @Override
        public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
            super.clearCachedAuthorizationInfo(principals);
        }
    
        @Override
        public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
            super.clearCachedAuthenticationInfo(principals);
        }
    
        @Override
        public void clearCache(PrincipalCollection principals) {
            super.clearCache(principals);
        }
    
        protected Subject getCurrentExecutingSubject(){
            return SecurityUtils.getSubject();
        }
    
        protected Session getShiroSession(Subject subject){
            return subject.getSession();
        }
    
    }
    
    

    自定义授权handler类实现AbstractUsernamePasswordAuthenticationHandler抽象类:

    package org.muses.jeeplatform.cas.authentication.handler;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.apereo.cas.authentication.*;
    import org.apereo.cas.authentication.AuthenticationException;
    import org.apereo.cas.authentication.exceptions.AccountDisabledException;
    import org.apereo.cas.authentication.handler.support.AbstractPreAndPostProcessingAuthenticationHandler;
    import org.apereo.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
    import org.apereo.cas.authentication.principal.PrincipalFactory;
    import org.apereo.cas.services.ServicesManager;
    
    import javax.security.auth.login.AccountLockedException;
    import javax.security.auth.login.AccountNotFoundException;
    import javax.security.auth.login.CredentialExpiredException;
    import javax.security.auth.login.FailedLoginException;
    import java.security.GeneralSecurityException;
    
    /**
     * <pre>
     *
     * </pre>
     *
     * <pre>
     * @author mazq
     * 修改记录
     *    修改后版本:     修改人:  修改日期: 2020/04/26 11:03  修改内容:
     * </pre>
     */
    public class ShiroAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler {
    
        public ShiroAuthenticationHandler(String name, ServicesManager servicesManager, PrincipalFactory principalFactory, Integer order) {
            super(name, servicesManager, principalFactory, order);
        }
    
        @Override
        protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(UsernamePasswordCredential credential, String originalPassword) throws GeneralSecurityException, PreventedException {
            try {
                UsernamePasswordToken token = new UsernamePasswordToken(credential.getUsername(), credential.getPassword());
    
                if (credential instanceof RememberMeUsernamePasswordCredential) {
                    token.setRememberMe(RememberMeUsernamePasswordCredential.class.cast(credential).isRememberMe());
                }
    
                Subject subject = getCurrentExecutingSubject();
                subject.login(token);
    
                //获取Shiro管理的Session
                //Session session = getShiroSession(subject);
    
                final String username = subject.getPrincipal().toString();
                return createHandlerResult(credential, this.principalFactory.createPrincipal(username));
            } catch (final UnknownAccountException uae) {
                throw new AccountNotFoundException(uae.getMessage());
            } catch (final IncorrectCredentialsException ice) {
                throw new FailedLoginException(ice.getMessage());
            } catch (final LockedAccountException | ExcessiveAttemptsException lae) {
                throw new AccountLockedException(lae.getMessage());
            } catch (final ExpiredCredentialsException eae) {
                throw new CredentialExpiredException(eae.getMessage());
            } catch (final DisabledAccountException eae) {
                throw new AccountDisabledException(eae.getMessage());
            } catch (final AuthenticationException e) {
                throw new FailedLoginException(e.getMessage());
            }
        }
    
        protected Subject getCurrentExecutingSubject(){
            return SecurityUtils.getSubject();
        }
    
        protected Session getShiroSession(Subject subject){
            return subject.getSession();
        }
    
    
        @Override
        public boolean supports(Credential credential) {
            return false;
        }
    }
    
    

    同理实现一个Shiro配置类:

    package org.muses.jeeplatform.cas.authentication.config;
    
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
    import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer;
    import org.apereo.cas.authentication.AuthenticationHandler;
    import org.apereo.cas.authentication.PrePostAuthenticationHandler;
    import org.apereo.cas.authentication.principal.DefaultPrincipalFactory;
    import org.apereo.cas.configuration.CasConfigurationProperties;
    import org.apereo.cas.services.ServicesManager;
    import org.muses.jeeplatform.cas.authentication.handler.ShiroAuthenticationHandler;
    import org.muses.jeeplatform.cas.authentication.handler.UsernamePasswordAuthenticationHandler;
    import org.muses.jeeplatform.cas.authentication.shiro.ShiroAuthorizingRealm;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * <pre>
     *
     * </pre>
     *
     * <pre>
     * @author mazq
     * 修改记录
     *    修改后版本:     修改人:  修改日期: 2020/04/26 16:35  修改内容:
     * </pre>
     */
    @Configuration("ShiroAuthenticationConfiguration")
    @EnableConfigurationProperties(CasConfigurationProperties.class)
    public class ShiroAuthenticationConfiguration implements AuthenticationEventExecutionPlanConfigurer  {
        @Autowired
        private CasConfigurationProperties casProperties;
    
        @Autowired
        @Qualifier("servicesManager")
        private ServicesManager servicesManager;
    
        //@Bean
        public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            shiroFilterFactoryBean.setSecurityManager(securityManager);
    
            //拦截器.
            Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
            // 配置不会被拦截的链接 顺序判断
            filterChainDefinitionMap.put("/static/**", "anon");
            filterChainDefinitionMap.put("/upload/**", "anon");
            filterChainDefinitionMap.put("/plugins/**", "anon");
            filterChainDefinitionMap.put("/code", "anon");
            filterChainDefinitionMap.put("/login", "anon");
            filterChainDefinitionMap.put("/logincheck", "anon");
            filterChainDefinitionMap.put("/**", "authc");
    
            shiroFilterFactoryBean.setLoginUrl("/login");
            shiroFilterFactoryBean.setSuccessUrl("/index");
            shiroFilterFactoryBean.setUnauthorizedUrl("/login");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        }
    
        @Bean
        public ShiroAuthorizingRealm shiroAuthorizingRealm(){
            ShiroAuthorizingRealm myShiroRealm = new ShiroAuthorizingRealm();
            //myShiroRealm.setCachingEnabled(false);
            //启用身份验证缓存,即缓存AuthenticationInfo信息,默认false
            myShiroRealm.setAuthenticationCachingEnabled(false);
            //启用授权缓存,即缓存AuthorizationInfo信息,默认false
            myShiroRealm.setAuthorizationCachingEnabled(false);
            return myShiroRealm;
        }
    
    
        @Bean
        public SecurityManager securityManager(){
            DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
            securityManager.setRealm(shiroAuthorizingRealm());
            return securityManager;
        }
    
        /**
         * Spring静态注入
         * @return
         */
        @Bean
        public MethodInvokingFactoryBean getMethodInvokingFactoryBean(){
            MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
            factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
            factoryBean.setArguments(new Object[]{securityManager()});
            return factoryBean;
        }
    
        @Bean
        public AuthenticationHandler myAuthenticationHandler() {
            return new ShiroAuthenticationHandler(ShiroAuthenticationHandler.class.getName(),
                    servicesManager, new DefaultPrincipalFactory(), 1);
        }
    
        @Override
        public void configureAuthenticationExecutionPlan(AuthenticationEventExecutionPlan plan) {
            plan.registerAuthenticationHandler(myAuthenticationHandler());
        }
    
    
    
    }
    
    

    在META-INF文件夹,新增一个命名为spring.factories的文件

    在这里插入图片描述
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.muses.jeeplatform.cas.authentication.config.ShiroAuthenticationConfiguration
    
    

    为什么要这样做?因为这样做才能将配置类加载到spring容器,详情需要跟下源码,可以参考我博客:SpringBoot源码学习系列之自动配置原理简介

    在这里插入图片描述 在这里插入图片描述

    代码例子参考:github下载链接

    详情可以参考官方文档:https://apereo.github.io/cas/5.3.x/installation/Configuration-Properties.html

    优质参考博客:
    https://www.cnblogs.com/jpeanut/tag/CAS/
    https://blog.csdn.net/anumbrella/category_7765386.html

    相关文章

      网友评论

        本文标题:CAS 5.3.1系列之自定义Shiro认证策略(四)

        本文链接:https://www.haomeiwen.com/subject/vndgwhtx.html