diff --git a/tamguo/src/main/java/com/tamguo/config/shiro/MemberRealm.java b/tamguo/src/main/java/com/tamguo/config/shiro/MemberRealm.java index e697481..42a7b65 100644 --- a/tamguo/src/main/java/com/tamguo/config/shiro/MemberRealm.java +++ b/tamguo/src/main/java/com/tamguo/config/shiro/MemberRealm.java @@ -21,53 +21,80 @@ import com.tamguo.service.IMemberService; /** * 认证 - * + * 这个类 `MemberRealm` 继承自 `AuthorizingRealm`,是 Apache Shiro 框架中用于进行认证(登录验证)和授权(权限验证)相关操作的核心类, + * 它负责与业务逻辑层交互,获取用户信息并进行相应的验证判断等操作。 */ public class MemberRealm extends AuthorizingRealm { - + + // 通过Spring的依赖注入机制,自动注入 `IMemberService` 接口的实现类实例, + // 用于后续在认证和授权过程中与数据库等数据源交互,获取用户相关信息(如用户实体、登录失败次数等)。 @Autowired private IMemberService iMemberService; - - /** - * 授权(验证权限时调用) - */ + + /** + * 授权(验证权限时调用) + * 这个方法重写了父类 `AuthorizingRealm` 的 `doGetAuthorizationInfo` 方法,用于获取用户的授权信息,即权限相关的数据。 + * 在 Shiro 框架验证用户权限时会调用此方法,根据用户的身份信息(通常通过 `PrincipalCollection` 传递)来确定该用户拥有哪些权限。 + * 当前实现比较简单,只是初始化了一个 `SimpleAuthorizationInfo` 对象,并且设置了权限集合(这里暂时设置为空集合,实际应用中需根据业务逻辑填充具体权限)。 + */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { - Set permsSet = null; + // 初始化一个用于存储权限字符串的集合,当前示例中暂未填充具体的权限数据,实际业务中需要从数据库等数据源获取用户对应的权限并添加到这个集合中。 + Set permsSet = null; SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); + // 将权限集合设置到 `SimpleAuthorizationInfo` 对象中,用于后续权限验证时判断用户是否具有特定权限。 info.setStringPermissions(permsSet); - + return info; } /** * 认证(登录时调用) + * 重写了父类 `AuthorizingRealm` 的 `doGetAuthenticationInfo` 方法,用于进行用户登录认证操作。 + * 当用户尝试登录系统时,Shiro 框架会调用这个方法来验证用户提供的登录信息(用户名和密码)是否正确, + * 该方法会与业务逻辑层交互,获取用户实体信息,并根据一系列规则判断登录是否合法,如用户名是否存在、密码是否正确、账号是否被锁定等。 */ @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { + // 从 `AuthenticationToken` 中获取用户输入的用户名,在 Shiro 中,`AuthenticationToken` 通常是在登录时传递用户名和密码等登录凭证的对象, + // 这里将获取到的用户名强制转换为 `String` 类型,因为假设登录时用户名是以字符串形式传递的(实际应用中需根据具体的 `AuthenticationToken` 实现来确定类型转换是否正确)。 String username = (String) token.getPrincipal(); - String password = new String((char[]) token.getCredentials()); - - MemberEntity member = iMemberService.findByUsername(username); - if(member == null) { - throw new UnknownAccountException("用户名或密码有误,请重新输入或找回密码"); - } - Integer loginFailureCount = iMemberService.getLoginFailureCount(member); - if(loginFailureCount > 10) { - throw new LockedAccountException("账号被锁定"); - } - - if(!new Sha256Hash(password).toHex().equals(member.getPassword())){ + // 从 `AuthenticationToken` 中获取用户输入的密码,同样需要进行类型转换,将其转换为 `String` 类型,这里将 `char[]` 类型的密码转换为 `String`, + // 注意这种方式在实际应用中可能存在安全风险(比如密码在内存中会以明文形式存在一段时间),更安全的做法是使用合适的加密算法立即对密码进行处理,避免明文暴露。 + String password = new String((char[]) token.getCredentials()); + + // 通过注入的 `IMemberService` 调用 `findByUsername` 方法,根据用户名从数据库或其他数据源查找对应的 `MemberEntity`(用户实体对象), + // 如果返回 `null`,说明用户名不存在,此时抛出 `UnknownAccountException`,表示用户名或密码有误(在实际应用中可以更细化错误提示,区分是用户名不存在还是密码错误等情况)。 + MemberEntity member = iMemberService.findByUsername(username); + if (member == null) { + throw new UnknownAccountException("用户名或密码有误,请重新输入或找回密码"); + } + + // 通过 `IMemberService` 调用 `getLoginFailureCount` 方法获取当前用户的登录失败次数, + // 如果登录失败次数大于 10 次,说明账号可能存在异常风险,抛出 `LockedAccountException`,表示账号被锁定,禁止登录。 + Integer loginFailureCount = iMemberService.getLoginFailureCount(member); + if (loginFailureCount > 10) { + throw new LockedAccountException("账号被锁定"); + } + + // 使用 `Sha256Hash` 对用户输入的密码进行哈希处理(将密码转换为不可逆的哈希值),并与数据库中存储的用户密码(假设数据库中存储的也是经过相同哈希算法处理后的哈希值)进行比较, + // 如果不相等,说明密码错误,此时增加登录失败次数,并通过 `IMemberService` 的 `updateLoginFailureCount` 方法更新数据库中的登录失败次数记录, + // 然后抛出 `IncorrectCredentialsException`,提示用户名或密码有误,请重新输入或找回密码。 + if (!new Sha256Hash(password).toHex().equals(member.getPassword())) { loginFailureCount++; - iMemberService.updateLoginFailureCount(member , loginFailureCount); + iMemberService.updateLoginFailureCount(member, loginFailureCount); throw new IncorrectCredentialsException("用户名或密码有误,请重新输入或找回密码"); } + + // 如果密码正确,调用 `IMemberService` 的 `updateLastLoginTime` 方法更新用户的最后登录时间,记录此次登录操作, + // 然后创建一个 `SimpleAuthenticationInfo` 对象,将用户实体对象(`member`)、密码(`password`)以及当前 `Realm` 的名称(通过 `getName()` 获取)传递进去, + // 这个对象会被 Shiro 框架用于后续的认证相关流程,比如在会话中保存认证信息等操作。 // 更新登录时间 iMemberService.updateLastLoginTime(member.getUid().toString()); - - SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(member, password, getName()); - return info; + + SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(member, password, getName()); + return info; } -} +} \ No newline at end of file diff --git a/tamguo/src/main/java/com/tamguo/config/shiro/ShiroConfiguration.java b/tamguo/src/main/java/com/tamguo/config/shiro/ShiroConfiguration.java index 430d260..6b44a16 100644 --- a/tamguo/src/main/java/com/tamguo/config/shiro/ShiroConfiguration.java +++ b/tamguo/src/main/java/com/tamguo/config/shiro/ShiroConfiguration.java @@ -14,15 +14,26 @@ import org.springframework.context.annotation.Configuration; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; +// 这个类使用了Spring的 @Configuration 注解,表明它是一个配置类,用于配置Shiro框架相关的各种组件和功能, +// Spring在启动时会自动扫描并加载这个类中的配置信息,将其中定义的Bean注册到Spring容器中。 @Configuration public class ShiroConfiguration { + // 创建一个LinkedHashMap用于定义Shiro的过滤链规则,键是URL路径的匹配模式,值是对应的权限过滤器名称, + // 后续会将这个映射关系配置到ShiroFilterFactoryBean中,用于控制不同URL的访问权限。 private static Map filterChainDefinitionMap = new LinkedHashMap(); + // 定义一个名为 "shiroRealm" 的Bean,这个Bean返回的是自定义的MemberRealm实例, + // MemberRealm是继承自Shiro的AuthorizingRealm,用于实现具体的认证(登录验证)和授权(权限验证)逻辑, + // 将其注册为Spring容器中的Bean,方便其他Shiro相关组件引用,比如在SecurityManager中设置使用这个Realm进行验证操作。 @Bean(name = "shiroRealm") public MemberRealm getShiroRealm() { return new MemberRealm(); } + // 定义一个名为 "shiroEhcacheManager" 的Bean,用于创建和配置EhCacheManager实例, + // EhCacheManager在Shiro框架中用于缓存相关的数据,比如用户的授权信息等,提高系统性能,减少重复查询授权等操作的开销。 + // 通过设置cacheManagerConfigFile属性指定了EhCache的配置文件路径(这里是类路径下的ehcache-shiro.xml文件), + // 该配置文件会定义缓存的具体策略、缓存区域等相关设置。 @Bean(name = "shiroEhcacheManager") public EhCacheManager getEhCacheManager() { EhCacheManager em = new EhCacheManager(); @@ -30,11 +41,17 @@ public class ShiroConfiguration { return em; } + // 定义一个名为 "lifecycleBeanPostProcessor" 的Bean,创建LifecycleBeanPostProcessor实例, + // LifecycleBeanPostProcessor是Shiro提供的一个用于管理Shiro相关Bean生命周期的后置处理器, + // 它能够确保Shiro的一些组件(如Realm等)在Spring容器中正确地初始化、销毁等,按照其生命周期规范执行相应操作。 @Bean(name = "lifecycleBeanPostProcessor") public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } + // 定义一个名为 "defaultAdvisorAutoProxyCreator" 的Bean,创建DefaultAdvisorAutoProxyCreator实例, + // 它是Spring AOP中的一个自动代理创建器,用于在Spring容器中自动创建基于Advisor(切面顾问)的代理对象, + // 在Shiro与Spring集成时,帮助实现基于AOP的权限控制拦截等功能,通过设置proxyTargetClass为true,表示使用基于类的代理方式(而不是基于接口的代理)。 @Bean public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator(); @@ -42,6 +59,10 @@ public class ShiroConfiguration { return daap; } + // 定义一个名为 "securityManager" 的Bean,创建DefaultWebSecurityManager实例, + // DefaultWebSecurityManager是Shiro在Web应用中用于管理安全相关操作的核心组件, + // 在这里设置了它所使用的Realm(通过调用getShiroRealm()方法获取前面定义的MemberRealm实例)以及缓存管理器(通过getEhCacheManager()获取EhCacheManager实例), + // 这样SecurityManager在进行认证和授权操作时就会使用指定的Realm进行验证,并利用缓存管理器来缓存相关信息,提高效率。 @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager() { DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager(); @@ -50,22 +71,36 @@ public class ShiroConfiguration { return dwsm; } + // 定义一个名为 "authorizationAttributeSourceAdvisor" 的Bean,创建AuthorizationAttributeSourceAdvisor实例, + // AuthorizationAttributeSourceAdvisor是Shiro与Spring AOP集成的一个关键组件,用于在方法级别进行权限控制, + // 它通过设置securityManager(调用getDefaultWebSecurityManager()获取配置好的SecurityManager)来关联整个权限管理体系, + // 使得在Spring应用中可以基于注解等方式方便地进行权限控制,比如在方法上添加Shiro的权限注解来限制哪些用户可以访问该方法。 @Bean public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor(); aasa.setSecurityManager(getDefaultWebSecurityManager()); return new AuthorizationAttributeSourceAdvisor(); } - - /** - * ShiroDialect,为了在thymeleaf里使用shiro的标签的bean - * @return - */ - @Bean - public ShiroDialect shiroDialect(){ + + /** + * ShiroDialect,为了在thymeleaf里使用shiro的标签的bean + * 这个方法定义了一个名为 "shiroDialect" 的Bean,返回ShiroDialect实例, + * ShiroDialect是用于在Thymeleaf模板引擎中使用Shiro标签的一个方言类, + * 当项目中使用Thymeleaf作为页面模板并且需要在页面中进行Shiro相关的权限判断、显示等操作时(比如根据用户权限显示或隐藏页面元素), + * 通过将这个Bean注册到Spring容器中,Thymeleaf就能识别并解析Shiro相关的标签,实现相应的功能。 + */ + @Bean + public ShiroDialect shiroDialect() { return new ShiroDialect(); } + // 定义一个名为 "shiroFilter" 的Bean,创建ShiroFilterFactoryBean实例, + // ShiroFilterFactoryBean是Shiro在Web应用中用于配置URL级别的访问控制过滤器链的核心组件, + // 它通过设置securityManager(关联前面配置好的DefaultWebSecurityManager)来建立整个权限过滤的基础框架, + // 同时设置了登录页面的URL(通过setLoginUrl方法设置为 "/login",当用户未登录访问受保护资源时会重定向到这个登录页面)、 + // 登录成功后的默认跳转页面URL(通过setSuccessUrl方法设置为 "/index"), + // 并且将前面定义的filterChainDefinitionMap(包含了URL路径和对应权限过滤器的映射关系)设置进去, + // 这样Shiro就能根据这些配置对不同的URL请求进行权限验证和相应的处理,比如哪些URL需要认证才能访问,哪些URL可以匿名访问等。 @Bean(name = "shiroFilter") public ShiroFilterFactoryBean getShiroFilterFactoryBean() { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();