Shiro多realm实际
开发中忽然接到一个新的需要,须要在原始零碎中退出一个单点登录的性能,这不是很简略的一个需要吗,几行代码就能够搞定的事。拿到文档后间接开搞,发现用户方提供了一个token查问用户信息的接口,那么需要变得更清晰了,写一个链接,参数就是用户方的token,完事拿着token去申请平台接口,将获取到的用户信息间接注册到平台。上面写一点伪代码意思一下:
public void sso(String token){ Result result = HttpUtil.post(url+token); if(result.isOk()){ User user = result.data(); if(!check(user)){ // 如果用户在零碎中不存在 register(user); } //将单点登录的用户登录零碎中 login(user.getLoginName(),user.getPassword()); }}
以上代码根本解决了登录问题,原本这样就能够交差了,然而忽然想到,零碎还有锁屏以及定时批改明码等问题,因而单点登录须要绕过明码登录,然而零碎应用的是shiro,realm是用的明码和账号受权很显然无奈满足了。因而笔者想到了以下两种计划:
- 单点登录实现,主动初始化明码,保障库内明码统一
- 革新零碎登录模块,减少独自针对sso的realm
很显然,这里第二种计划更靠谱,上面开始登录革新。
一、创立单点登录须要的Token类
应用shiro登录时默认会应用UsernamePasswordToken,咱们这边为了实现疏忽明码的形式,这里间接抉择继承BearerToken,进行自定义token,这里贴出局部代码,能够依据本人需要就行更改
public class SSOToken extends BearerToken { private SysUser user; public SSOToken(SysUser user,String token) { super(token); this.user = user; } public SysUser getUser() { return user; } public void setUser(SysUser user) { this.user = user; }}
二、创立单点登录应用的realm
家喻户晓,shiro登录及权限获取的逻辑次要是在realm中实现,默认状况下,咱们创立一个realm用于明码登录即可,这里为实现用户名明码和token两种登录形式,须要创立两个realm,此处本人贴出自定义代码,读者可按需批改
public class SSORealm extends AuthorizingRealm implements Serializable { /** * 受权 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); Set<String> roles = new HashSet<>(); roles.add("admin"); info.setRoles(roles); return info; } /** * 登录认证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { SSOToken upToken = (SSOToken) token; // 获取sso登录传入的user实体 User user = upToken.getUser(); //不传入回绝登录 if(user==null){ throw new UnknownAccountException(); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, upToken.getToken(), getName()); return info; }}
三、自定义身份验证器
编写一个自定义的身份验证器,依据条件抉择指定的realm进行登录验证。
public enum LoginType { SSO("SSORealm"),USERNAME("UserRealm"); private final String type; LoginType(String type) { this.type = type; } @Override public String toString() { return type; }}@Componentpublic class CustomModularRealmAuthenticator extends ModularRealmAuthenticator { @Override protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException { assertRealmsConfigured(); Collection<Realm> realms = getRealms(); Collection<Realm> typeRealms = new ArrayList<>(); if (authenticationToken instanceof SSOToken) { // 单点登录形式抉择SSORealm for (Realm realm : realms) { if (realm.getName().contains(LoginType.SSO.toString())) { typeRealms.add(realm); } } } else if (authenticationToken instanceof UsernamePasswordToken) { //用户名明码形式登录,抉择UserRealm for (Realm realm : realms) { if (realm.getName().contains(LoginType.USERNAME.toString())) { typeRealms.add(realm); } } } if (typeRealms.size() == 1) { return doSingleRealmAuthentication(typeRealms.iterator().next(), authenticationToken); } return doMultiRealmAuthentication(typeRealms, authenticationToken); }}
四、编写配置文件
实现上述工作后,多realm配置根本实现了,不过因为用到了BearToken,还须要对shiro的配置类进行一些革新
@Configurationpublic class ShiroConfig { @Autowired private ModularRealmAuthenticator customModularRealmAuthenticator; /** * 自定义Realm */ @Bean public UserRealm userRealm() { UserRealm userRealm = new UserRealm(); return userRealm; } @Bean public SSORealm ssoRealm() { SSORealm userRealm = new SSORealm(); userRealm.setAuthenticationTokenClass(BearerToken.class); return userRealm; } /** * 平安管理器 */ @Bean public DefaultWebSecurityManager securityManager(Collection<Realm> realms) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //设置多realm辨认 securityManager.setAuthenticator(customModularRealmAuthenticator); // 设置realm. securityManager.setRealms(realms); return securityManager; } @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // Shiro的外围平安接口,这个属性是必须的 shiroFilterFactoryBean.setSecurityManager(securityManager); // 身份认证失败,则跳转到登录页面的配置 shiroFilterFactoryBean.setLoginUrl("/login"); // 权限认证失败,则跳转到指定页面 shiroFilterFactoryBean.setUnauthorizedUrl("/unauth"); // Shiro连贯束缚配置,即过滤链的定义 LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/normal", "anon"); filterChainDefinitionMap.put("/sso", "anon"); filterChainDefinitionMap.put("/**", "authc"); Map<String, Filter> filters = new LinkedHashMap<String, Filter>(); shiroFilterFactoryBean.setFilters(filters); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; }}
五、总结
至此,单点登录的多realm实现根本实现了,因为我的项目起因,贴出的代码根本都是示例,读者能够按需进行批改,须要新增的就是realm、自定义身份验证器,还有就是须要对shiro config进行一些调整,全副的代码示例曾经上传到github,如有须要能够自行下载。