diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java index 1652f74ef..f881a9ba2 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java @@ -2,6 +2,9 @@ package com.ruoyi.web.controller.system; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + +import com.ruoyi.framework.shiro.LoginType; +import com.ruoyi.framework.shiro.UserToken; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; @@ -17,7 +20,7 @@ import com.ruoyi.common.utils.StringUtils; /** * 登录验证 - * + * * @author ruoyi */ @Controller @@ -39,22 +42,9 @@ public class SysLoginController extends BaseController @ResponseBody public AjaxResult ajaxLogin(String username, String password, Boolean rememberMe) { - UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe); - Subject subject = SecurityUtils.getSubject(); - try - { - subject.login(token); - return success(); - } - catch (AuthenticationException e) - { - String msg = "用户或密码错误"; - if (StringUtils.isNotEmpty(e.getMessage())) - { - msg = e.getMessage(); - } - return error(msg); - } +// UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe); + UserToken token = new UserToken(username, password, rememberMe, LoginType.USER_PASSWORD); + return shiroLogin(token); } @GetMapping("/unauth") @@ -62,4 +52,35 @@ public class SysLoginController extends BaseController { return "error/unauth"; } + + /** + * 手机验证码登录 + * 注:由于是demo演示,此处不添加发送验证码方法; + * 正常操作:发送验证码至手机并且将验证码存放在redis中,登录的时候比较用户穿过来的验证码和redis中存放的验证码 + * + * @param phone + * @param code + * @return + */ + @PostMapping("/phoneLogin") + @ResponseBody + public AjaxResult phoneLogin(String phone, String code) { + // 此处phone替换了username,code替换了password + UserToken token = new UserToken(phone, code, LoginType.USER_PHONE); + return shiroLogin(token); + } + + public AjaxResult shiroLogin(UserToken token) { + Subject subject = SecurityUtils.getSubject(); + try { + subject.login(token); + return success(); + } catch (AuthenticationException e) { + String msg = "用户或密码错误"; + if (StringUtils.isNotEmpty(e.getMessage())) { + msg = e.getMessage(); + } + return error(msg); + } + } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java index 7d26b5ff0..0b29dda2e 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java @@ -1,28 +1,12 @@ package com.ruoyi.framework.config; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.LinkedHashMap; -import java.util.Map; -import javax.servlet.Filter; -import org.apache.commons.io.IOUtils; -import org.apache.shiro.cache.ehcache.EhCacheManager; -import org.apache.shiro.codec.Base64; -import org.apache.shiro.config.ConfigurationException; -import org.apache.shiro.io.ResourceUtils; -import org.apache.shiro.mgt.SecurityManager; -import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; -import org.apache.shiro.spring.web.ShiroFilterFactoryBean; -import org.apache.shiro.web.mgt.CookieRememberMeManager; -import org.apache.shiro.web.mgt.DefaultWebSecurityManager; -import org.apache.shiro.web.servlet.SimpleCookie; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.framework.shiro.LoginType; +import com.ruoyi.framework.shiro.MyModularRealmAuthenticator; +import com.ruoyi.framework.shiro.realm.AuthorizationRealm; +import com.ruoyi.framework.shiro.realm.UserPhoneRealm; import com.ruoyi.framework.shiro.realm.UserRealm; import com.ruoyi.framework.shiro.session.OnlineSessionDAO; import com.ruoyi.framework.shiro.session.OnlineSessionFactory; @@ -32,16 +16,41 @@ import com.ruoyi.framework.shiro.web.filter.online.OnlineSessionFilter; import com.ruoyi.framework.shiro.web.filter.sync.SyncOnlineSessionFilter; import com.ruoyi.framework.shiro.web.session.OnlineWebSessionManager; import com.ruoyi.framework.shiro.web.session.SpringSessionValidationScheduler; -import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; +import org.apache.commons.io.IOUtils; +import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy; +import org.apache.shiro.cache.ehcache.EhCacheManager; +import org.apache.shiro.codec.Base64; +import org.apache.shiro.config.ConfigurationException; +import org.apache.shiro.io.ResourceUtils; +import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.realm.AuthorizingRealm; +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.web.mgt.CookieRememberMeManager; +import org.apache.shiro.web.mgt.DefaultWebSecurityManager; +import org.apache.shiro.web.servlet.SimpleCookie; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.servlet.Filter; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; /** * 权限配置加载 - * + * * @author ruoyi */ @Configuration -public class ShiroConfig -{ +public class ShiroConfig { public static final String PREMISSION_STRING = "perms[\"{0}\"]"; // Session超时时间,单位为毫秒(默认30分钟) @@ -84,17 +93,13 @@ public class ShiroConfig * 缓存管理器 使用Ehcache实现 */ @Bean - public EhCacheManager getEhCacheManager() - { + public EhCacheManager getEhCacheManager() { net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("ruoyi"); EhCacheManager em = new EhCacheManager(); - if (StringUtils.isNull(cacheManager)) - { + if (StringUtils.isNull(cacheManager)) { em.setCacheManager(new net.sf.ehcache.CacheManager(getCacheManagerConfigFileInputStream())); return em; - } - else - { + } else { em.setCacheManager(cacheManager); return em; } @@ -103,24 +108,18 @@ public class ShiroConfig /** * 返回配置文件流 避免ehcache配置文件一直被占用,无法完全销毁项目重新部署 */ - protected InputStream getCacheManagerConfigFileInputStream() - { + protected InputStream getCacheManagerConfigFileInputStream() { String configFile = "classpath:ehcache/ehcache-shiro.xml"; InputStream inputStream = null; - try - { + try { inputStream = ResourceUtils.getInputStreamForPath(configFile); byte[] b = IOUtils.toByteArray(inputStream); InputStream in = new ByteArrayInputStream(b); return in; - } - catch (IOException e) - { + } catch (IOException e) { throw new ConfigurationException( "Unable to obtain input stream for cacheManagerConfigFile [" + configFile + "]", e); - } - finally - { + } finally { IOUtils.closeQuietly(inputStream); } } @@ -129,8 +128,7 @@ public class ShiroConfig * 自定义Realm */ @Bean - public UserRealm userRealm(EhCacheManager cacheManager) - { + public UserRealm userRealm(EhCacheManager cacheManager) { UserRealm userRealm = new UserRealm(); userRealm.setCacheManager(cacheManager); return userRealm; @@ -140,8 +138,7 @@ public class ShiroConfig * 自定义sessionDAO会话 */ @Bean - public OnlineSessionDAO sessionDAO() - { + public OnlineSessionDAO sessionDAO() { OnlineSessionDAO sessionDAO = new OnlineSessionDAO(); return sessionDAO; } @@ -150,8 +147,7 @@ public class ShiroConfig * 自定义sessionFactory会话 */ @Bean - public OnlineSessionFactory sessionFactory() - { + public OnlineSessionFactory sessionFactory() { OnlineSessionFactory sessionFactory = new OnlineSessionFactory(); return sessionFactory; } @@ -160,8 +156,7 @@ public class ShiroConfig * 会话管理器 */ @Bean - public OnlineWebSessionManager sessionManager() - { + public OnlineWebSessionManager sessionManager() { OnlineWebSessionManager manager = new OnlineWebSessionManager(); // 加入缓存管理器 manager.setCacheManager(getEhCacheManager()); @@ -186,11 +181,22 @@ public class ShiroConfig * 安全管理器 */ @Bean - public SecurityManager securityManager(UserRealm userRealm, SpringSessionValidationScheduler springSessionValidationScheduler) - { + public SecurityManager securityManager(UserRealm userRealm, SpringSessionValidationScheduler springSessionValidationScheduler) { + DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 设置realm. - securityManager.setRealm(userRealm); +// securityManager.setRealm(userRealm); + // 修改为多realm + securityManager.setAuthenticator(modularRealmAuthenticator()); + List realms = new ArrayList<>(); + // 统一角色权限控制realm + realms.add(authorizingRealm()); + // 用户密码登录realm + realms.add(userRealm()); + // 短信登录 + realms.add(userPhoneRealm()); + securityManager.setRealms(realms); + // 记住我 securityManager.setRememberMeManager(rememberMeManager()); // 注入缓存管理器; @@ -203,8 +209,7 @@ public class ShiroConfig /** * 退出过滤器 */ - public LogoutFilter logoutFilter() - { + public LogoutFilter logoutFilter() { LogoutFilter logoutFilter = new LogoutFilter(); logoutFilter.setLoginUrl(loginUrl); return logoutFilter; @@ -214,8 +219,7 @@ public class ShiroConfig * Shiro过滤器配置 */ @Bean - public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) - { + public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // Shiro的核心安全接口,这个属性是必须的 shiroFilterFactoryBean.setSecurityManager(securityManager); @@ -241,6 +245,8 @@ public class ShiroConfig filterChainDefinitionMap.put("/logout", "logout"); // 不需要拦截的访问 filterChainDefinitionMap.put("/login", "anon,captchaValidate"); + // 不需要拦截的访问 + filterChainDefinitionMap.put("/phoneLogin", "anon,captchaValidate"); // 系统权限列表 // filterChainDefinitionMap.putAll(SpringUtils.getBean(IMenuService.class).selectPermsAll()); @@ -263,8 +269,7 @@ public class ShiroConfig * 自定义在线用户处理过滤器 */ @Bean - public OnlineSessionFilter onlineSessionFilter() - { + public OnlineSessionFilter onlineSessionFilter() { OnlineSessionFilter onlineSessionFilter = new OnlineSessionFilter(); onlineSessionFilter.setLoginUrl(loginUrl); return onlineSessionFilter; @@ -274,8 +279,7 @@ public class ShiroConfig * 自定义在线用户同步过滤器 */ @Bean - public SyncOnlineSessionFilter syncOnlineSessionFilter() - { + public SyncOnlineSessionFilter syncOnlineSessionFilter() { SyncOnlineSessionFilter syncOnlineSessionFilter = new SyncOnlineSessionFilter(); return syncOnlineSessionFilter; } @@ -284,8 +288,7 @@ public class ShiroConfig * 自定义验证码过滤器 */ @Bean - public CaptchaValidateFilter captchaValidateFilter() - { + public CaptchaValidateFilter captchaValidateFilter() { CaptchaValidateFilter captchaValidateFilter = new CaptchaValidateFilter(); captchaValidateFilter.setCaptchaEnabled(captchaEnabled); captchaValidateFilter.setCaptchaType(captchaType); @@ -295,8 +298,7 @@ public class ShiroConfig /** * cookie 属性设置 */ - public SimpleCookie rememberMeCookie() - { + public SimpleCookie rememberMeCookie() { SimpleCookie cookie = new SimpleCookie("rememberMe"); cookie.setDomain(domain); cookie.setPath(path); @@ -308,8 +310,7 @@ public class ShiroConfig /** * 记住我 */ - public CookieRememberMeManager rememberMeManager() - { + public CookieRememberMeManager rememberMeManager() { CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(rememberMeCookie()); cookieRememberMeManager.setCipherKey(Base64.decode("fCq+/xW488hMTCD+cmJ3aQ==")); @@ -320,8 +321,7 @@ public class ShiroConfig * thymeleaf模板引擎和shiro框架的整合 */ @Bean - public ShiroDialect shiroDialect() - { + public ShiroDialect shiroDialect() { return new ShiroDialect(); } @@ -330,10 +330,57 @@ public class ShiroConfig */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor( - @Qualifier("securityManager") SecurityManager securityManager) - { + @Qualifier("securityManager") SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } + + /** + * 自定义的Realm管理,主要针对多realm + */ + @Bean("myModularRealmAuthenticator") + public MyModularRealmAuthenticator modularRealmAuthenticator() { + MyModularRealmAuthenticator customizedModularRealmAuthenticator = new MyModularRealmAuthenticator(); + // 设置realm判断条件 + customizedModularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy()); + + return customizedModularRealmAuthenticator; + } + + @Bean + public AuthorizingRealm authorizingRealm() { + AuthorizationRealm authorizationRealm = new AuthorizationRealm(); + authorizationRealm.setName(LoginType.COMMON.getType()); + + return authorizationRealm; + } + + /** + * 账号密码登录realm + * + * @return + */ + @Bean + public UserRealm userRealm() { + UserRealm userRealm = new UserRealm(); + userRealm.setName(LoginType.USER_PASSWORD.getType()); + + return userRealm; + } + + /** + * 手机号验证码登录realm + * + * @return + */ + @Bean + public UserPhoneRealm userPhoneRealm() { + UserPhoneRealm userPhoneRealm = new UserPhoneRealm(); + userPhoneRealm.setName(LoginType.USER_PHONE.getType()); + + return userPhoneRealm; + } + + } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/LoginType.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/LoginType.java new file mode 100644 index 000000000..4656d1fc4 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/LoginType.java @@ -0,0 +1,36 @@ +package com.ruoyi.framework.shiro; + +/** + * @Description 登录方式的枚举类,存放我们所有的登录方式 + * https://blog.csdn.net/zhourenfei17/article/details/88826911 + * @Author 胡浩 + * @Date 2019/6/3 + **/ +public enum LoginType { + /** 通用 **/ + COMMON("common_realm"), + /** 用户密码登录 **/ + USER_PASSWORD("user_password_realm"), + /** 手机验证码登录 **/ + USER_PHONE("user_phone_realm"), + /** 二维码登录 **/ + QRCODE_LOGIN("qrcode_login_realm"), + /** 第三方登录(微信登录) **/ + WECHAT_LOGIN("wechat_login_realm"); + + private String type; + + private LoginType(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + @Override + public String toString() { + return this.type.toString(); + } + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/MyModularRealmAuthenticator.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/MyModularRealmAuthenticator.java new file mode 100644 index 000000000..0be7053f5 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/MyModularRealmAuthenticator.java @@ -0,0 +1,43 @@ +package com.ruoyi.framework.shiro; + +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.pam.ModularRealmAuthenticator; +import org.apache.shiro.realm.Realm; + +import java.util.Collection; +import java.util.HashMap; + +/** + * @Description + * 创建自定义的多relam的管理策略 + * @Author 胡浩 + * @Date 2019/6/3 + **/ +public class MyModularRealmAuthenticator extends ModularRealmAuthenticator { + @Override + protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException { + // 判断getRealms()是否返回为空 + assertRealmsConfigured(); + + // 所有Realm + Collection realms = getRealms(); + // 登录类型对应的所有Realm + HashMap realmHashMap = new HashMap<>(realms.size()); + for (Realm realm : realms) { + realmHashMap.put(realm.getName(), realm); + } + + UserToken token = (UserToken) authenticationToken; + // 登录类型 + LoginType loginType = token.getLoginType(); + + if (realmHashMap.get(loginType.getType()) != null) { + return doSingleRealmAuthentication(realmHashMap.get(loginType.getType()), token); + } else { + return doMultiRealmAuthentication(realms, token); + } + } + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/UserToken.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/UserToken.java new file mode 100644 index 000000000..bf67dd1e0 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/UserToken.java @@ -0,0 +1,55 @@ +package com.ruoyi.framework.shiro; + +import org.apache.shiro.authc.UsernamePasswordToken; + +/** + * @Description + * @Author 胡浩 + * @Date 2019/6/3 + **/ +public class UserToken extends UsernamePasswordToken { + /** + * 登录方式 + **/ + private LoginType loginType; + /** + * 微信code + **/ + private String code; + + public UserToken(final String username, final String password, LoginType loginType) { + super(username, password); + this.loginType = loginType; + } + + /** + * 记住我 + **/ + public UserToken(String username, String password, boolean rememberMe, LoginType loginType) { + super(username, password, rememberMe); + this.loginType = loginType; + } + + // TODO 由于是demo方法,此处微信只传一个code参数,其他参数根据实际情况添加 + public UserToken(String username, String password, String code, LoginType loginType) { + super(username, password); + this.loginType = loginType; + this.code = code; + } + + public LoginType getLoginType() { + return loginType; + } + + public void setLoginType(LoginType loginType) { + this.loginType = loginType; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/realm/AuthorizationRealm.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/realm/AuthorizationRealm.java new file mode 100644 index 000000000..323c4e21d --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/realm/AuthorizationRealm.java @@ -0,0 +1,75 @@ +package com.ruoyi.framework.shiro.realm; + +import com.ruoyi.framework.util.ShiroUtils; +import com.ruoyi.system.domain.SysUser; +import com.ruoyi.system.service.ISysMenuService; +import com.ruoyi.system.service.ISysRoleService; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.HashSet; +import java.util.Set; + +/** + * @Description 统一角色授权管理realm + * 当我们往shiro的SecurityManager中注入多个realm后,当一个新用户调用需授权的方法时, + * 会依次调用所有的realm的doGetAuthoriztionInfo方法, + * 而不是根据登录类型来调用不同realm的授权方法;因此为了避免重复代码(或者说重复授权),我们这边做统一处理。 + * @Author 胡浩 + * @Date 2019/6/4 + **/ +public class AuthorizationRealm extends AuthorizingRealm { + @Autowired + private ISysMenuService menuService; + + @Autowired + private ISysRoleService roleService; + + /** + * 授权 + */ + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { + SysUser user = ShiroUtils.getSysUser(); + // 角色列表 + Set roles = new HashSet(); + // 功能列表 + Set menus = new HashSet(); + SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); + // 管理员拥有所有权限 + if (user.isAdmin()) { + info.addRole("admin"); + info.addStringPermission("*:*:*"); + } else { + roles = roleService.selectRoleKeys(user.getUserId()); + menus = menuService.selectPermsByUserId(user.getUserId()); + // 角色加入AuthorizationInfo认证对象 + info.setRoles(roles); + // 权限加入AuthorizationInfo认证对象 + info.setStringPermissions(menus); + } + return info; + } + + /** + * 清理缓存权限 + */ + public void clearCachedAuthorizationInfo() { + this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals()); + } + + /** + * 认证信息.(身份验证) : Authentication 是用来验证用户身份 + */ + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { + return null; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/realm/UserPhoneRealm.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/realm/UserPhoneRealm.java new file mode 100644 index 000000000..c0f76b621 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/realm/UserPhoneRealm.java @@ -0,0 +1,93 @@ +package com.ruoyi.framework.shiro.realm; + +import com.ruoyi.common.exception.user.*; +import com.ruoyi.framework.shiro.LoginType; +import com.ruoyi.framework.shiro.UserToken; +import com.ruoyi.framework.shiro.service.SysLoginService; +import com.ruoyi.system.domain.SysUser; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authc.*; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @Description + * @Author 胡浩 + * @Date 2019/6/4 + **/ +public class UserPhoneRealm extends AuthorizingRealm { + private static final Logger log = LoggerFactory.getLogger(UserPhoneRealm.class); + + + @Autowired + private SysLoginService loginService; + + @Override + public String getName() { + return LoginType.USER_PHONE.getType(); + } + + @Override + public boolean supports(AuthenticationToken token) { + if (token instanceof UserToken) { + return ((UserToken) token).getLoginType() == LoginType.USER_PHONE; + } else { + return false; + } + } + + /** + * 授权 + */ + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { + return null; + } + + /** + * 登录认证 + */ + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { + UserToken upToken = (UserToken) token; +// UsernamePasswordToken upToken = (UsernamePasswordToken) token; + String phone = upToken.getUsername(); + String validCode = ""; + if (upToken.getPassword() != null) { + validCode = new String(upToken.getPassword()); + } + + SysUser user = null; + try { + user = loginService.phoneLogin(phone, validCode); + } catch (CaptchaException e) { + throw new AuthenticationException(e.getMessage(), e); + } catch (UserNotExistsException e) { + throw new UnknownAccountException(e.getMessage(), e); + } catch (UserPasswordNotMatchException e) { + throw new IncorrectCredentialsException(e.getMessage(), e); + } catch (UserPasswordRetryLimitExceedException e) { + throw new ExcessiveAttemptsException(e.getMessage(), e); + } catch (UserBlockedException e) { + throw new LockedAccountException(e.getMessage(), e); + } catch (RoleBlockedException e) { + throw new LockedAccountException(e.getMessage(), e); + } catch (Exception e) { + log.info("对用户[" + phone + "]进行登录验证..验证未通过{}", e.getMessage()); + throw new AuthenticationException(e.getMessage(), e); + } + SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, validCode, getName()); + return info; + } + + /** + * 清理缓存权限 + */ + public void clearCachedAuthorizationInfo() { + this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals()); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/realm/UserRealm.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/realm/UserRealm.java index 258efba3f..6c5bdb09a 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/realm/UserRealm.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/realm/UserRealm.java @@ -1,44 +1,27 @@ package com.ruoyi.framework.shiro.realm; -import java.util.HashSet; -import java.util.Set; +import com.ruoyi.common.exception.user.*; +import com.ruoyi.framework.shiro.LoginType; +import com.ruoyi.framework.shiro.UserToken; +import com.ruoyi.framework.shiro.service.SysLoginService; +import com.ruoyi.system.domain.SysUser; +import com.ruoyi.system.service.ISysMenuService; +import com.ruoyi.system.service.ISysRoleService; import org.apache.shiro.SecurityUtils; -import org.apache.shiro.authc.AuthenticationException; -import org.apache.shiro.authc.AuthenticationInfo; -import org.apache.shiro.authc.AuthenticationToken; -import org.apache.shiro.authc.ExcessiveAttemptsException; -import org.apache.shiro.authc.IncorrectCredentialsException; -import org.apache.shiro.authc.LockedAccountException; -import org.apache.shiro.authc.SimpleAuthenticationInfo; -import org.apache.shiro.authc.UnknownAccountException; -import org.apache.shiro.authc.UsernamePasswordToken; +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.subject.PrincipalCollection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import com.ruoyi.common.exception.user.CaptchaException; -import com.ruoyi.common.exception.user.RoleBlockedException; -import com.ruoyi.common.exception.user.UserBlockedException; -import com.ruoyi.common.exception.user.UserNotExistsException; -import com.ruoyi.common.exception.user.UserPasswordNotMatchException; -import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException; -import com.ruoyi.framework.shiro.service.SysLoginService; -import com.ruoyi.framework.util.ShiroUtils; -import com.ruoyi.system.domain.SysUser; -import com.ruoyi.system.service.ISysMenuService; -import com.ruoyi.system.service.ISysRoleService; - /** * 自定义Realm 处理登录 权限 - * + * * @author ruoyi */ -public class UserRealm extends AuthorizingRealm -{ +public class UserRealm extends AuthorizingRealm { private static final Logger log = LoggerFactory.getLogger(UserRealm.class); @Autowired @@ -50,81 +33,75 @@ public class UserRealm extends AuthorizingRealm @Autowired private SysLoginService loginService; + @Override + public String getName() { + return LoginType.USER_PASSWORD.getType(); + } + + @Override + public boolean supports(AuthenticationToken token) { + if (token instanceof UserToken) { + return ((UserToken) token).getLoginType() == LoginType.USER_PASSWORD; + } else { + return false; + } + } + /** * 授权 */ @Override - protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) - { - SysUser user = ShiroUtils.getSysUser(); - // 角色列表 - Set roles = new HashSet(); - // 功能列表 - Set menus = new HashSet(); - SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); - // 管理员拥有所有权限 - if (user.isAdmin()) - { - info.addRole("admin"); - info.addStringPermission("*:*:*"); - } - else - { - roles = roleService.selectRoleKeys(user.getUserId()); - menus = menuService.selectPermsByUserId(user.getUserId()); - // 角色加入AuthorizationInfo认证对象 - info.setRoles(roles); - // 权限加入AuthorizationInfo认证对象 - info.setStringPermissions(menus); - } - return info; + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { +// SysUser user = ShiroUtils.getSysUser(); +// // 角色列表 +// Set roles = new HashSet(); +// // 功能列表 +// Set menus = new HashSet(); +// SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); +// // 管理员拥有所有权限 +// if (user.isAdmin()) { +// info.addRole("admin"); +// info.addStringPermission("*:*:*"); +// } else { +// roles = roleService.selectRoleKeys(user.getUserId()); +// menus = menuService.selectPermsByUserId(user.getUserId()); +// // 角色加入AuthorizationInfo认证对象 +// info.setRoles(roles); +// // 权限加入AuthorizationInfo认证对象 +// info.setStringPermissions(menus); +// } +// return info; + return null; } /** * 登录认证 */ @Override - protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException - { + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken upToken = (UsernamePasswordToken) token; String username = upToken.getUsername(); String password = ""; - if (upToken.getPassword() != null) - { + if (upToken.getPassword() != null) { password = new String(upToken.getPassword()); } SysUser user = null; - try - { + try { user = loginService.login(username, password); - } - catch (CaptchaException e) - { + } catch (CaptchaException e) { throw new AuthenticationException(e.getMessage(), e); - } - catch (UserNotExistsException e) - { + } catch (UserNotExistsException e) { throw new UnknownAccountException(e.getMessage(), e); - } - catch (UserPasswordNotMatchException e) - { + } catch (UserPasswordNotMatchException e) { throw new IncorrectCredentialsException(e.getMessage(), e); - } - catch (UserPasswordRetryLimitExceedException e) - { + } catch (UserPasswordRetryLimitExceedException e) { throw new ExcessiveAttemptsException(e.getMessage(), e); - } - catch (UserBlockedException e) - { + } catch (UserBlockedException e) { throw new LockedAccountException(e.getMessage(), e); - } - catch (RoleBlockedException e) - { + } catch (RoleBlockedException e) { throw new LockedAccountException(e.getMessage(), e); - } - catch (Exception e) - { + } catch (Exception e) { log.info("对用户[" + username + "]进行登录验证..验证未通过{}", e.getMessage()); throw new AuthenticationException(e.getMessage(), e); } @@ -135,8 +112,7 @@ public class UserRealm extends AuthorizingRealm /** * 清理缓存权限 */ - public void clearCachedAuthorizationInfo() - { + public void clearCachedAuthorizationInfo() { this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals()); } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/service/SysLoginService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/service/SysLoginService.java index 92ebe89d8..a8f17a219 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/service/SysLoginService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/service/SysLoginService.java @@ -21,6 +21,9 @@ import com.ruoyi.framework.util.ShiroUtils; import com.ruoyi.system.domain.SysUser; import com.ruoyi.system.service.ISysUserService; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * 登录校验方法 * @@ -133,4 +136,60 @@ public class SysLoginService user.setLoginDate(DateUtils.getNowDate()); userService.updateUserInfo(user); } + + /** + * 手机验证码登录 + */ + public SysUser phoneLogin(String phone, String validCode) { + + //TODO 验证验证码 +// // 验证码校验 +// if (!StringUtils.isEmpty(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA))) { +// AsyncManager.me().execute(AsyncFactory.recordLogininfor(phone, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); +// throw new CaptchaException(); +// } + + // 手机或验证码为空 错误 + if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(validCode)) { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(phone, Constants.LOGIN_FAIL, MessageUtils.message("not.null"))); + throw new UserNotExistsException(); + } + + String regex = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(17[013678])|(18[0,5-9]))\\d{8}$"; + if (phone.length() != 11) { + System.out.println("手机号应为11位数"); + } else { + Pattern p = Pattern.compile(regex); + Matcher m = p.matcher(phone); + boolean isMatch = m.matches(); + if (isMatch) { + System.out.println("您的手机号" + phone + "是正确格式@——@"); + } else { + System.out.println("您的手机号" + phone + "是错误格式!!!"); + } + } + + + // 查询用户信息 + SysUser user = userService.selectUserByPhoneNumber(phone); + + if (user == null) { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(phone, Constants.LOGIN_FAIL, MessageUtils.message("user.not.exists"))); + throw new UserNotExistsException(); + } + + if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(phone, Constants.LOGIN_FAIL, MessageUtils.message("user.password.delete"))); + throw new UserDeleteException(); + } + + if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(phone, Constants.LOGIN_FAIL, MessageUtils.message("user.blocked", user.getRemark()))); + throw new UserBlockedException(); + } + + AsyncManager.me().execute(AsyncFactory.recordLogininfor(phone, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); + recordLoginInfo(user); + return user; + } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/util/ShiroUtils.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/util/ShiroUtils.java index a57f1197b..8a29f8cab 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/util/ShiroUtils.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/util/ShiroUtils.java @@ -1,5 +1,6 @@ package com.ruoyi.framework.util; +import com.ruoyi.framework.shiro.realm.AuthorizationRealm; import org.apache.shiro.SecurityUtils; import org.apache.shiro.crypto.SecureRandomNumberGenerator; import org.apache.shiro.mgt.RealmSecurityManager; @@ -60,7 +61,8 @@ public class ShiroUtils public static void clearCachedAuthorizationInfo() { RealmSecurityManager rsm = (RealmSecurityManager) SecurityUtils.getSecurityManager(); - UserRealm realm = (UserRealm) rsm.getRealms().iterator().next(); +// UserRealm realm = (UserRealm) rsm.getRealms().iterator().next(); + AuthorizationRealm realm = (AuthorizationRealm) rsm.getRealms().iterator().next(); realm.clearCachedAuthorizationInfo(); }