CAS 5.3.1系列之自定义Shiro认证策略(四)
CAS官方文档是介绍基于配置实现shiro认证的,可以参考官方文档,不过我们也可以通过自定义认证策略的方式实现jdbc认证,pom先加入相关jar
1<!-- Custom Authentication --> 2 <dependency> 3 <groupId>org.apereo.cas</groupId> 4 <artifactId>cas-server-core-authentication-api</artifactId> 5 <version>${cas.version}</version> 6 </dependency> 7 8 <!-- Custom Configuration --> 9 <dependency> 10 <groupId>org.apereo.cas</groupId> 11 <artifactId>cas-server-core-configuration-api</artifactId> 12 <version>${cas.version}</version> 13 </dependency> 14 15 <dependency> 16 <groupId>org.apereo.cas</groupId> 17 <artifactId>cas-server-support-generic</artifactId> 18 <version>${cas.version}</version> 19 </dependency>
如果要用公网提供的基于配置实现的,需要加入:
1<!-- Shiro Authentication --> 2 <dependency> 3 <groupId>org.apereo.cas</groupId> 4 <artifactId>cas-server-support-shiro-authentication</artifactId> 5 <version>${cas.version}</version> 6 </dependency>
要自定义shiroRealm的,加入shiro相关jar:
1<dependency> 2 <groupId>org.apache.shiro</groupId> 3 <artifactId>shiro-spring</artifactId> 4 <version>1.4.0</version> 5 </dependency>
实现一个Shiro Realm类,仅供参考:
1import org.apache.shiro.SecurityUtils; 2import org.apache.shiro.authc.*; 3import org.apache.shiro.authz.AuthorizationInfo; 4import org.apache.shiro.authz.SimpleAuthorizationInfo; 5import org.apache.shiro.realm.AuthorizingRealm; 6import org.apache.shiro.session.Session; 7import org.apache.shiro.subject.PrincipalCollection; 8import org.apache.shiro.subject.Subject; 9import org.muses.jeeplatform.cas.user.model.User; 10import org.muses.jeeplatform.cas.user.service.UserService; 11import org.slf4j.Logger; 12import org.slf4j.LoggerFactory; 13import org.springframework.beans.factory.annotation.Autowired; 14import org.springframework.beans.factory.annotation.Qualifier; 15import org.springframework.jdbc.core.BeanPropertyRowMapper; 16import org.springframework.jdbc.core.JdbcTemplate; 17import org.springframework.jdbc.datasource.DriverManagerDataSource; 18 19/** 20 * <pre> 21 * 22 * </pre> 23 * 24 * <pre> 25 * @author mazq 26 * 修改记录 27 * 修改后版本: 修改人: 修改日期: 2020/04/26 11:33 修改内容: 28 * </pre> 29 */ 30public class ShiroAuthorizingRealm extends AuthorizingRealm { 31 32 33 34 35 Logger LOG = LoggerFactory.getLogger(ShiroAuthorizingRealm.class); 36 37 /**注解引入业务类**/ 38 //@Autowired 39 //UserService userService; 40 41 /** 42 * 登录信息和用户验证信息验证(non-Javadoc) 43 * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(AuthenticationToken) 44 */ 45 @Override 46 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 47 48 49 50 51 String username = (String)token.getPrincipal(); //得到用户名 52 String password = new String((char[])token.getCredentials()); //得到密码 53 54 LOG.info("Shiro doGetAuthenticationInfo>> username:{},password:{}",username,password); 55 56 //User user = userService.findByUsername(username); 57 // JDBC模板依赖于连接池来获得数据的连接,所以必须先要构造连接池 58 DriverManagerDataSource dataSource = new DriverManagerDataSource(); 59 dataSource.setDriverClassName("com.mysql.jdbc.Driver"); 60 dataSource.setUrl("jdbc:mysql://192.168.0.152:33306/jeeplatform"); 61 dataSource.setUsername("root"); 62 dataSource.setPassword("minstone"); 63 64 // 创建JDBC模板 65 JdbcTemplate jdbcTemplate = new JdbcTemplate(); 66 jdbcTemplate.setDataSource(dataSource); 67 68 String sql = "SELECT * FROM sys_user WHERE username = ?"; 69 70 User user = (User) jdbcTemplate.queryForObject(sql, new Object[]{ 71 72 73 username}, new BeanPropertyRowMapper(User.class)); 74 Subject subject = getCurrentExecutingSubject(); 75 //获取Shiro管理的Session 76 Session session = getShiroSession(subject); 77 //Shiro添加会话 78 session.setAttribute("username", username); 79 session.setAttribute(ShiroConsts.SESSION_USER, user); 80 81 /**检测是否有此用户 **/ 82 if(user == null){ 83 84 85 86 throw new UnknownAccountException();//没有找到账号异常 87 } 88 /**检验账号是否被锁定 **/ 89 if(Boolean.TRUE.equals(user.getLocked())){ 90 91 92 93 throw new LockedAccountException();//抛出账号锁定异常 94 } 95 /**AuthenticatingRealm使用CredentialsMatcher进行密码匹配**/ 96 if(null != username && null != password){ 97 98 99 100 return new SimpleAuthenticationInfo(username, password, getName()); 101 }else{ 102 103 104 105 return null; 106 } 107 108 } 109 110 /** 111 * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法(non-Javadoc) 112 * @see AuthorizingRealm#doGetAuthorizationInfo(PrincipalCollection) 113 */ 114 @Override 115 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) { 116 117 118 119 String username = (String)pc.getPrimaryPrincipal(); 120 SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); 121// authorizationInfo.setRoles(userService.getRoles(username)); 122// authorizationInfo.setStringPermissions(userService.getPermissions(username)); 123 System.out.println("Shiro授权"); 124 return authorizationInfo; 125 } 126 127 @Override 128 public void clearCachedAuthorizationInfo(PrincipalCollection principals) { 129 130 131 132 super.clearCachedAuthorizationInfo(principals); 133 } 134 135 @Override 136 public void clearCachedAuthenticationInfo(PrincipalCollection principals) { 137 138 139 140 super.clearCachedAuthenticationInfo(principals); 141 } 142 143 @Override 144 public void clearCache(PrincipalCollection principals) { 145 146 147 148 super.clearCache(principals); 149 } 150 151 protected Subject getCurrentExecutingSubject(){ 152 153 154 155 return SecurityUtils.getSubject(); 156 } 157 158 protected Session getShiroSession(Subject subject){ 159 160 161 162 return subject.getSession(); 163 } 164 165} 166
自定义授权handler类实现AbstractUsernamePasswordAuthenticationHandler抽象类:
1package org.muses.jeeplatform.cas.authentication.handler; 2 3import org.apache.shiro.SecurityUtils; 4import org.apache.shiro.authc.*; 5import org.apache.shiro.session.Session; 6import org.apache.shiro.subject.Subject; 7import org.apereo.cas.authentication.*; 8import org.apereo.cas.authentication.AuthenticationException; 9import org.apereo.cas.authentication.exceptions.AccountDisabledException; 10import org.apereo.cas.authentication.handler.support.AbstractPreAndPostProcessingAuthenticationHandler; 11import org.apereo.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler; 12import org.apereo.cas.authentication.principal.PrincipalFactory; 13import org.apereo.cas.services.ServicesManager; 14 15import javax.security.auth.login.AccountLockedException; 16import javax.security.auth.login.AccountNotFoundException; 17import javax.security.auth.login.CredentialExpiredException; 18import javax.security.auth.login.FailedLoginException; 19import java.security.GeneralSecurityException; 20 21/** 22 * <pre> 23 * 24 * </pre> 25 * 26 * <pre> 27 * @author mazq 28 * 修改记录 29 * 修改后版本: 修改人: 修改日期: 2020/04/26 11:03 修改内容: 30 * </pre> 31 */ 32public class ShiroAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler { 33 34 35 36 37 public ShiroAuthenticationHandler(String name, ServicesManager servicesManager, PrincipalFactory principalFactory, Integer order) { 38 39 40 41 super(name, servicesManager, principalFactory, order); 42 } 43 44 @Override 45 protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(UsernamePasswordCredential credential, String originalPassword) throws GeneralSecurityException, PreventedException { 46 47 48 49 try { 50 51 52 53 UsernamePasswordToken token = new UsernamePasswordToken(credential.getUsername(), credential.getPassword()); 54 55 if (credential instanceof RememberMeUsernamePasswordCredential) { 56 57 58 59 token.setRememberMe(RememberMeUsernamePasswordCredential.class.cast(credential).isRememberMe()); 60 } 61 62 Subject subject = getCurrentExecutingSubject(); 63 subject.login(token); 64 65 //获取Shiro管理的Session 66 //Session session = getShiroSession(subject); 67 68 final String username = subject.getPrincipal().toString(); 69 return createHandlerResult(credential, this.principalFactory.createPrincipal(username)); 70 } catch (final UnknownAccountException uae) { 71 72 73 74 throw new AccountNotFoundException(uae.getMessage()); 75 } catch (final IncorrectCredentialsException ice) { 76 77 78 79 throw new FailedLoginException(ice.getMessage()); 80 } catch (final LockedAccountException | ExcessiveAttemptsException lae) { 81 82 83 84 throw new AccountLockedException(lae.getMessage()); 85 } catch (final ExpiredCredentialsException eae) { 86 87 88 89 throw new CredentialExpiredException(eae.getMessage()); 90 } catch (final DisabledAccountException eae) { 91 92 93 94 throw new AccountDisabledException(eae.getMessage()); 95 } catch (final AuthenticationException e) { 96 97 98 99 throw new FailedLoginException(e.getMessage()); 100 } 101 } 102 103 protected Subject getCurrentExecutingSubject(){ 104 105 106 107 return SecurityUtils.getSubject(); 108 } 109 110 protected Session getShiroSession(Subject subject){ 111 112 113 114 return subject.getSession(); 115 } 116 117 118 @Override 119 public boolean supports(Credential credential) { 120 121 122 123 return false; 124 } 125} 126
同理实现一个Shiro配置类:
1package org.muses.jeeplatform.cas.authentication.config; 2 3import org.apache.shiro.mgt.SecurityManager; 4import org.apache.shiro.spring.web.ShiroFilterFactoryBean; 5import org.apache.shiro.web.mgt.DefaultWebSecurityManager; 6import org.apereo.cas.authentication.AuthenticationEventExecutionPlan; 7import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer; 8import org.apereo.cas.authentication.AuthenticationHandler; 9import org.apereo.cas.authentication.PrePostAuthenticationHandler; 10import org.apereo.cas.authentication.principal.DefaultPrincipalFactory; 11import org.apereo.cas.configuration.CasConfigurationProperties; 12import org.apereo.cas.services.ServicesManager; 13import org.muses.jeeplatform.cas.authentication.handler.ShiroAuthenticationHandler; 14import org.muses.jeeplatform.cas.authentication.handler.UsernamePasswordAuthenticationHandler; 15import org.muses.jeeplatform.cas.authentication.shiro.ShiroAuthorizingRealm; 16import org.springframework.beans.factory.annotation.Autowired; 17import org.springframework.beans.factory.annotation.Qualifier; 18import org.springframework.beans.factory.config.MethodInvokingFactoryBean; 19import org.springframework.boot.context.properties.EnableConfigurationProperties; 20import org.springframework.context.annotation.Bean; 21import org.springframework.context.annotation.Configuration; 22 23import java.util.LinkedHashMap; 24import java.util.Map; 25 26/** 27 * <pre> 28 * 29 * </pre> 30 * 31 * <pre> 32 * @author mazq 33 * 修改记录 34 * 修改后版本: 修改人: 修改日期: 2020/04/26 16:35 修改内容: 35 * </pre> 36 */ 37@Configuration("ShiroAuthenticationConfiguration") 38@EnableConfigurationProperties(CasConfigurationProperties.class) 39public class ShiroAuthenticationConfiguration implements AuthenticationEventExecutionPlanConfigurer { 40 41 42 43 @Autowired 44 private CasConfigurationProperties casProperties; 45 46 @Autowired 47 @Qualifier("servicesManager") 48 private ServicesManager servicesManager; 49 50 //@Bean 51 public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { 52 53 54 55 ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); 56 shiroFilterFactoryBean.setSecurityManager(securityManager); 57 58 //拦截器. 59 Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>(); 60 // 配置不会被拦截的链接 顺序判断 61 filterChainDefinitionMap.put("/static/**", "anon"); 62 filterChainDefinitionMap.put("/upload/**", "anon"); 63 filterChainDefinitionMap.put("/plugins/**", "anon"); 64 filterChainDefinitionMap.put("/code", "anon"); 65 filterChainDefinitionMap.put("/login", "anon"); 66 filterChainDefinitionMap.put("/logincheck", "anon"); 67 filterChainDefinitionMap.put("/**", "authc"); 68 69 shiroFilterFactoryBean.setLoginUrl("/login"); 70 shiroFilterFactoryBean.setSuccessUrl("/index"); 71 shiroFilterFactoryBean.setUnauthorizedUrl("/login"); 72 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); 73 return shiroFilterFactoryBean; 74 } 75 76 @Bean 77 public ShiroAuthorizingRealm shiroAuthorizingRealm(){ 78 79 80 81 ShiroAuthorizingRealm myShiroRealm = new ShiroAuthorizingRealm(); 82 //myShiroRealm.setCachingEnabled(false); 83 //启用身份验证缓存,即缓存AuthenticationInfo信息,默认false 84 myShiroRealm.setAuthenticationCachingEnabled(false); 85 //启用授权缓存,即缓存AuthorizationInfo信息,默认false 86 myShiroRealm.setAuthorizationCachingEnabled(false); 87 return myShiroRealm; 88 } 89 90 91 @Bean 92 public SecurityManager securityManager(){ 93 94 95 96 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 97 securityManager.setRealm(shiroAuthorizingRealm()); 98 return securityManager; 99 } 100 101 /** 102 * Spring静态注入 103 * @return 104 */ 105 @Bean 106 public MethodInvokingFactoryBean getMethodInvokingFactoryBean(){ 107 108 109 110 MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean(); 111 factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager"); 112 factoryBean.setArguments(new Object[]{ 113 114 115 securityManager()}); 116 return factoryBean; 117 } 118 119 @Bean 120 public AuthenticationHandler myAuthenticationHandler() { 121 122 123 124 return new ShiroAuthenticationHandler(ShiroAuthenticationHandler.class.getName(), 125 servicesManager, new DefaultPrincipalFactory(), 1); 126 } 127 128 @Override 129 public void configureAuthenticationExecutionPlan(AuthenticationEventExecutionPlan plan) { 130 131 132 133 plan.registerAuthenticationHandler(myAuthenticationHandler()); 134 } 135 136 137 138} 139
在META-INF文件夹,新增一个命名为spring.factories的文件

1org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2org.muses.jeeplatform.cas.authentication.config.ShiroAuthenticationConfiguration 3
为什么要这样做?因为这样做才能将配置类加载到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
本文同步分享在 博客“smileNicky”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。