readme 修改

Signed-off-by: SmileToCandy <smiletocandy@qq.com>
会员中心dcx
Dcx12138 8 months ago
parent da8010bf69
commit ab369351a6

@ -10,26 +10,58 @@ import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
// 这是Spring Boot应用的主启动类使用了 @SpringBootApplication 注解,该注解是一个组合注解,相当于同时使用了 @Configuration、@EnableAutoConfiguration 和 @ComponentScan 注解,
// 意味着它标记这个类是一个配置类会自动配置Spring Boot应用的很多默认设置并且会自动扫描和加载应用中的各个组件如各种Bean、Controller、Service等
@SpringBootApplication @SpringBootApplication
// 通过 @ComponentScan 注解指定要扫描的基础包路径,这里设置为 "com.tamguo"表示Spring会在该包及其子包下查找带有Spring相关注解如 @Component、@Service、@Controller 等的类并将它们注册为Spring容器中的组件方便后续进行依赖注入等操作。
@ComponentScan("com.tamguo") @ComponentScan("com.tamguo")
public class Application { public class Application {
/**
* Spring Boot SpringApplicationBuilder Application
* run argsSpring BootSpringWebWeb
*
* @param args
*/
public static void main(String[] args) { public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).run(args); new SpringApplicationBuilder(Application.class).run(args);
} }
/** /**
* FastJsonJackson * FastJsonJackson
* @return * JSONHttpMessageConvertersBean
* Spring Boot使JacksonJSON使FastJsonJacksonJSON
* JSON使JSON便
*
* @return HttpMessageConvertersSpringHTTPJSONJavaJSONJSONJava
*/ */
@Bean @Bean
public HttpMessageConverters fastJsonHttpMessageConverters() { public HttpMessageConverters fastJsonHttpMessageConverters() {
// 创建一个FastJsonHttpMessageConverter实例它是基于FastJson实现的HTTP消息转换器用于将Java对象转换为FastJson格式的JSON字符串或者将接收到的FastJson格式的JSON字符串转换为Java对象
// 在后续配置中会设置它的相关属性使其按照项目需求进行JSON数据的转换操作。
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
// 创建一个FastJsonConfig实例用于配置FastJson的相关参数比如设置日期格式、序列化特性等通过该配置对象可以定制化FastJson序列化和反序列化的行为使其符合项目的具体要求。
FastJsonConfig fastJsonConfig = new FastJsonConfig(); FastJsonConfig fastJsonConfig = new FastJsonConfig();
// 设置FastJson序列化日期类型数据时的格式这里设置为 "yyyy-MM-dd HH:mm:ss"使得在将包含日期属性的Java对象转换为JSON字符串时日期字段会按照该格式进行序列化方便前端进行统一的日期格式展示和处理
// 例如数据库中的日期时间类型数据在转换为JSON发送给前端时会以指定的这种常见的格式化字符串形式呈现便于前端直接展示或者进行日期相关的操作如格式化、比较等
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss"); fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
// 设置FastJson的序列化特性这里通过SerializerFeature.DisableCircularReferenceDetect来禁用循环引用检测
// 当Java对象之间存在复杂的关联关系可能形成循环引用时比如对象A包含对象B对象B又包含对象A这样的情况默认情况下FastJson会进行循环引用检测并做相应处理
// 但有时候可能希望关闭这种检测根据具体业务场景和数据结构确定通过设置该特性可以改变FastJson在序列化时对循环引用的处理方式避免可能出现的序列化异常或者生成不符合预期的JSON结构等问题。
fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect); fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
// 将配置好的FastJsonConfig对象设置到FastJsonHttpMessageConverter中使得消息转换器在进行JSON数据转换时应用这些配置参数按照设定的日期格式和序列化特性来处理Java对象与JSON字符串之间的转换操作。
fastConverter.setFastJsonConfig(fastJsonConfig); fastConverter.setFastJsonConfig(fastJsonConfig);
// 将配置好的FastJsonHttpMessageConverter赋值给converter变量这里其实可以直接使用fastConverter赋值这一步略显多余但不影响功能实现可能是代码风格或者后续潜在扩展的考虑
// 后续会使用该变量来创建并返回HttpMessageConverters对象。
FastJsonHttpMessageConverter converter = fastConverter; FastJsonHttpMessageConverter converter = fastConverter;
// 创建并返回一个HttpMessageConverters对象将配置好的FastJsonHttpMessageConverter作为参数传入这样Spring容器就会识别并使用这个自定义的消息转换器来处理HTTP请求和响应中的JSON数据转换
// 替代默认的Jackson相关的消息转换器实现使用FastJson进行JSON数据处理的目的。
return new HttpMessageConverters(converter); return new HttpMessageConverters(converter);
} }

@ -19,23 +19,40 @@ import com.baomidou.mybatisplus.plugins.parser.tenant.TenantSqlParser;
import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue; import net.sf.jsqlparser.expression.LongValue;
// 标识这是一个Spring的配置类用于配置MyBatis Plus相关的功能和插件通过定义多个Bean方法来创建并配置不同的MyBatis Plus组件
// 例如性能分析拦截器、分页插件、元对象处理器以及SQL注入器等这些组件将被Spring容器管理并应用到MyBatis Plus的相关操作中影响数据库访问的行为和功能。
@Configuration @Configuration
// 配置MyBatis的Mapper扫描路径告诉MyBatis Plus去指定的包及其子包下扫描接口作为Mapper接口这样MyBatis Plus就能自动为这些Mapper接口生成对应的实现类并注入到Spring容器中方便进行数据库操作。
@MapperScan("com.tamguo.modules.*.dao*") @MapperScan("com.tamguo.modules.*.dao*")
public class MybatisPlusConfig { public class MybatisPlusConfig {
/**
* PerformanceInterceptorBeanMyBatis PlusSQL
* SQL便SQL便
*
* @return PerformanceInterceptorSpringMyBatis Plus
*/
@Bean @Bean
public PerformanceInterceptor performanceInterceptor() { public PerformanceInterceptor performanceInterceptor() {
return new PerformanceInterceptor(); return new PerformanceInterceptor();
} }
/** /**
* mybatis-plus<br> * PaginationInterceptorBeanMyBatis Plus
* http://mp.baomidou.com<br> * LIMIT使便使
* SQL
* <p>
* http://mp.baomidou.com
*
* @return PaginationInterceptorSpringSQL
*/ */
@Bean @Bean
public PaginationInterceptor paginationInterceptor() { public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
paginationInterceptor.setLocalPage(true);// 开启 PageHelper 的支持 // 设置开启PageHelper的支持这里的PageHelper可能是指与分页相关的某种兼容模式或者特定功能支持具体依赖于MyBatis Plus内部实现以及与其他分页相关框架的整合情况
// 通过开启此功能,可以更好地适配一些已有的分页相关的使用习惯或者与其他分页工具协同工作(具体根据实际业务场景确定)。
paginationInterceptor.setLocalPage(true);
/* /*
* SQL <br> * SQL <br>
* 1 cookie SQL <br> * 1 cookie SQL <br>
@ -45,24 +62,28 @@ public class MybatisPlusConfig {
tenantSqlParser.setTenantHandler(new TenantHandler() { tenantSqlParser.setTenantHandler(new TenantHandler() {
@Override @Override
public Expression getTenantId() { public Expression getTenantId() {
// 返回表示租户ID的表达式这里固定返回一个值为1L的LongValue表达式表示当前租户的ID为1实际业务中通常需要从合适的地方如请求头、Cookie等动态获取租户ID。
return new LongValue(1L); return new LongValue(1L);
} }
@Override @Override
public String getTenantIdColumn() { public String getTenantIdColumn() {
// 返回用于标识租户ID的数据库表列名这里指定为"course_id",意味着在多租户场景下,通过该列的值来区分不同租户的数据,即该列存储了租户的相关标识信息。
return "course_id"; return "course_id";
} }
@Override @Override
public boolean doTableFilter(String tableName) { public boolean doTableFilter(String tableName) {
// 这里可以判断是否过滤表 // 这里可以判断是否过滤表即根据表名决定是否应用多租户的过滤逻辑例如某些公共表可能不需要进行租户隔离可以返回true进行过滤不应用租户相关的SQL解析处理
// 当前直接返回true表示默认对所有表应用过滤逻辑实际业务中需要根据具体的表结构和业务需求来准确判断哪些表需要进行租户相关处理哪些表不需要。
return true; return true;
} }
}); });
// 将配置好的TenantSqlParser添加到SQL解析器列表中后续分页拦截器会遍历该列表中的解析器对SQL语句依次进行解析处理应用相应的逻辑如多租户数据隔离等
sqlParserList.add(tenantSqlParser); sqlParserList.add(tenantSqlParser);
paginationInterceptor.setSqlParserList(sqlParserList); paginationInterceptor.setSqlParserList(sqlParserList);
// 以下过滤方式与 @SqlParser(filter = true) 注解等效 // 以下过滤方式与 @SqlParser(filter = true) 注解等效
// paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() { // paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {
// @Override // @Override
@ -75,19 +96,31 @@ public class MybatisPlusConfig {
// return false; // return false;
// } // }
// }); // });
return paginationInterceptor; return paginationInterceptor;
} }
/**
* MetaObjectHandlerBeanMyBatis Plus
* Mapper
*
* @return MetaObjectHandlerMyMetaObjectHandlerMetaObjectHandler
* Spring
*/
@Bean @Bean
public MetaObjectHandler metaObjectHandler(){ public MetaObjectHandler metaObjectHandler() {
return new MyMetaObjectHandler(); return new MyMetaObjectHandler();
} }
/** /**
* sql * ISqlInjectorSQLBeanMyBatis PlusSQLLogicSqlInjector
* 便
* SQLMyBatis PlusMapper便使SQL
*
* @return LogicSqlInjectorSQLSpring使使MyBatis Plus使SQL
*/ */
@Bean @Bean
public ISqlInjector sqlInjector(){ public ISqlInjector sqlInjector() {
return new LogicSqlInjector(); return new LogicSqlInjector();
} }
} }

@ -1,6 +1,7 @@
package com.tamguo.config.shiro; package com.tamguo.config.shiro;
import java.util.Set; import java.util.Set;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.AuthenticationToken;
@ -20,20 +21,33 @@ import com.tamguo.modules.member.service.IMemberService;
/** /**
* *
* * ShiroAuthorizingRealm
* ShiroRealmShiro
* IMemberService
*/ */
public class MemberRealm extends AuthorizingRealm { public class MemberRealm extends AuthorizingRealm {
// 自动注入IMemberService用于调用会员相关的业务逻辑方法比如根据用户名查找会员信息、获取登录失败次数、更新登录失败次数、更新最后登录时间等操作
// 这些操作都是在认证(验证用户登录合法性)和授权(后续可能基于会员角色等信息获取权限)过程中需要依赖的业务逻辑实现。
@Autowired @Autowired
private IMemberService iMemberService; private IMemberService iMemberService;
/** /**
* () * ()
*/ * Shiro访Shiro
* SimpleAuthorizationInfo
* infoShiro
*
* @param principals
*
* @return AuthorizationInfoSimpleAuthorizationInfoShiro访
*/
@Override @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
Set<String > permsSet = null; // 目前只是初始化一个空的权限集合,实际业务中应从数据库等数据源查询并填充相应的权限信息,例如根据用户角色、用户直接关联的权限等获取权限列表。
Set<String> permsSet = null;
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 将权限集合设置到授权信息对象中,当前为空集合,表示没有赋予任何权限,实际情况需要根据业务逻辑查询并设置正确的权限信息。
info.setStringPermissions(permsSet); info.setStringPermissions(permsSet);
return info; return info;
@ -41,32 +55,54 @@ public class MemberRealm extends AuthorizingRealm {
/** /**
* () * ()
* ShiroShiro
*
* ShiroAuthenticationInfo
*
* @param token Shiro
* token.getPrincipal()token.getCredentials()
* @return AuthenticationInfoSimpleAuthenticationInfoRealmShiro
* AuthenticationExceptionShiro
* @throws AuthenticationException Shiro
*/ */
@Override @Override
protected AuthenticationInfo doGetAuthenticationInfo( protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
AuthenticationToken token) throws AuthenticationException { // 从登录凭证对象中获取用户名信息通常前端输入的用户名会被封装到token中作为主体信息这里将其从token中取出并转换为字符串类型用于后续查询数据库验证用户是否存在等操作。
String username = (String) token.getPrincipal(); String username = (String) token.getPrincipal();
String password = new String((char[]) token.getCredentials()); // 从登录凭证对象中获取密码信息token.getCredentials()返回的是字符数组类型,将其转换为字符串类型,用于后续与数据库中存储的用户密码进行比对验证操作。
String password = new String((char[]) token.getCredentials());
// 通过会员服务,根据用户名去数据库中查找对应的会员实体信息,用于后续验证该用户是否存在以及密码是否匹配等操作,如果查找不到则表示用户不存在,需要抛出相应异常。
MemberEntity member = iMemberService.findByUsername(username);
if (member == null) {
// 如果根据用户名未找到对应的会员信息抛出UnknownAccountException异常表示用户名不存在或者未找到对应的用户信息Shiro框架会捕获该异常并返回相应的错误提示给客户端。
throw new UnknownAccountException("用户名或密码有误,请重新输入或找回密码");
}
MemberEntity member = iMemberService.findByUsername(username); // 通过会员服务获取当前会员账号的登录失败次数用于后续判断账号是否因为多次登录失败而被锁定具体的锁定阈值这里是10次根据业务需求设定可调整。
if(member == null) { Integer loginFailureCount = iMemberService.getLoginFailureCount(member);
throw new UnknownAccountException("用户名或密码有误,请重新输入或找回密码"); if (loginFailureCount > 10) {
} // 如果登录失败次数超过设定的阈值10次抛出LockedAccountException异常表示账号被锁定Shiro框架会捕获该异常并返回相应的错误提示给客户端。
Integer loginFailureCount = iMemberService.getLoginFailureCount(member); throw new LockedAccountException("账号被锁定");
if(loginFailureCount > 10) { }
throw new LockedAccountException("账号被锁定");
}
if(!new Sha256Hash(password).toHex().equals(member.getPassword())){ // 将用户输入的密码进行哈希处理使用Sha256Hash算法进行加密这里将用户输入的密码转换为十六进制字符串形式方便与数据库中存储的加密后的密码进行比对
// 然后与数据库中存储的会员密码进行比对,如果不相等则表示密码错误,需要进行相应的错误处理操作(如记录登录失败次数、抛出异常等)。
if (!new Sha256Hash(password).toHex().equals(member.getPassword())) {
// 如果密码不匹配先将登录失败次数加1表示又一次登录失败然后调用会员服务更新数据库中该会员账号的登录失败次数记录。
loginFailureCount++; loginFailureCount++;
iMemberService.updateLoginFailureCount(member , loginFailureCount); iMemberService.updateLoginFailureCount(member, loginFailureCount);
// 抛出IncorrectCredentialsException异常表示密码错误Shiro框架会捕获该异常并返回相应的错误提示给客户端。
throw new IncorrectCredentialsException("用户名或密码有误,请重新输入或找回密码"); throw new IncorrectCredentialsException("用户名或密码有误,请重新输入或找回密码");
} }
// 更新登录时间
// 如果密码验证通过,调用会员服务更新数据库中该会员账号的最后登录时间,记录用户此次登录的时间信息,方便后续进行一些统计分析或者基于登录时间的业务逻辑处理(如判断账号是否长时间未登录等情况)。
iMemberService.updateLastLoginTime(member.getId()); iMemberService.updateLastLoginTime(member.getId());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(member, password, getName()); // 创建一个SimpleAuthenticationInfo对象用于返回认证成功的信息给Shiro框架将找到的会员实体对象member作为主体信息、用户输入的密码password以及当前Realm的名称通过getName()方法获取)设置到该对象中,
return info; // 表示登录认证成功Shiro框架后续会基于这些信息进行会话管理等相关操作并且可以在后续的请求处理中通过Shiro的相关API获取当前登录用户的信息等内容。
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(member, password, getName());
return info;
} }
} }

@ -12,27 +12,59 @@ import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreato
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
// 标识这是一个Spring的配置类用于配置Shiro框架相关的组件和功能通过定义多个Bean方法来创建并配置Shiro中的核心组件
// 如Realm用于认证和授权的领域对象、缓存管理器、安全管理器、过滤器工厂等这些组件协同工作为Web应用提供基于角色和权限的安全访问控制功能。
@Configuration @Configuration
public class ShiroConfiguration { public class ShiroConfiguration {
// 定义一个静态的LinkedHashMap用于存储URL路径与对应的Shiro过滤器链定义其中键表示URL的匹配模式值表示对应的过滤器链定义例如需要进行何种认证、授权等操作
// 后续会将该映射配置到ShiroFilterFactoryBean中用于控制不同URL的访问权限和安全策略。
private static Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); private static Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
/**
* MemberRealmRealmShiroRealmBean
* RealmSecurityManagerShiro
*
* @return MemberRealmSpringShiro使
*/
@Bean(name = "shiroRealm") @Bean(name = "shiroRealm")
public MemberRealm getShiroRealm() { public MemberRealm getShiroRealm() {
return new MemberRealm(); return new MemberRealm();
} }
/**
* EhCacheManagerEhCacheBeanShiro
* classpath:ehcache-shiro.xml
*
* @return EhCacheManagerSpringShiro使
*/
@Bean(name = "shiroEhcacheManager") @Bean(name = "shiroEhcacheManager")
public EhCacheManager getEhCacheManager() { public EhCacheManager getEhCacheManager() {
EhCacheManager em = new EhCacheManager(); EhCacheManager em = new EhCacheManager();
// 设置EhCache的配置文件路径该文件通常定义了缓存的名称、缓存的策略如内存缓存、磁盘缓存等、缓存的过期时间、最大缓存数量等相关配置信息
// Shiro会根据该配置文件来初始化和管理缓存这里使用classpath路径表示从类路径下查找该配置文件。
em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml"); em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
return em; return em;
} }
/**
* LifecycleBeanPostProcessorBeanBeanShiroSpring
* ShiroShiro
*
* @return LifecycleBeanPostProcessorSpring使Shiro
*/
@Bean(name = "lifecycleBeanPostProcessor") @Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor(); return new LifecycleBeanPostProcessor();
} }
/**
* DefaultAdvisorAutoProxyCreatorBeanSpring AOPAdvisor
* ShiroSpring使ShiroAOP
* proxyTargetClasstrue使
*
* @return DefaultAdvisorAutoProxyCreatorSpring使Spring AOPShiro
*/
@Bean @Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() { public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator(); DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
@ -40,14 +72,30 @@ public class ShiroConfiguration {
return daap; return daap;
} }
/**
* DefaultWebSecurityManagerWebBeanShiroWeb
* Realm
* MemberRealmEhCacheManager使Web访
*
* @return DefaultWebSecurityManagerSpringShiroShiroFilterFactoryBean
*/
@Bean(name = "securityManager") @Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager() { public DefaultWebSecurityManager getDefaultWebSecurityManager() {
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager(); DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
// 设置自定义的Realm将之前创建的MemberRealm实例注入到安全管理器中使得安全管理器在进行认证和授权操作时能够调用MemberRealm中实现的具体业务逻辑。
dwsm.setRealm(getShiroRealm()); dwsm.setRealm(getShiroRealm());
// 设置缓存管理器将之前创建的EhCacheManager实例注入到安全管理器中使得安全管理器能够利用缓存来管理用户认证信息、授权信息等提高系统性能和响应速度。
dwsm.setCacheManager(getEhCacheManager()); dwsm.setCacheManager(getEhCacheManager());
return dwsm; return dwsm;
} }
/**
* AuthorizationAttributeSourceAdvisorBeanShiroSpring AOP
* DefaultWebSecurityManager使Shiro
// 例如判断当前用户是否具有访问某个被@RequiresPermissions等授权注解标注的方法的权限从而实现细粒度的权限控制功能。
*
* @return AuthorizationAttributeSourceAdvisorSpring使Spring AOPShiro
*/
@Bean @Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() { public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor(); AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
@ -55,15 +103,30 @@ public class ShiroConfiguration {
return new AuthorizationAttributeSourceAdvisor(); return new AuthorizationAttributeSourceAdvisor();
} }
/**
* ShiroFilterFactoryBeanShiroBeanBeanShiroWebURL
* URLShiroWeb访URLURL
* Web访URL访URL访
*
* @return ShiroFilterFactoryBeanSpringWebWeb访
*/
@Bean(name = "shiroFilter") @Bean(name = "shiroFilter")
public ShiroFilterFactoryBean getShiroFilterFactoryBean() { public ShiroFilterFactoryBean getShiroFilterFactoryBean() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 设置安全管理器将之前创建的DefaultWebSecurityManager实例注入到ShiroFilterFactoryBean中使得过滤器工厂能够利用安全管理器进行认证、授权等安全相关的操作
// 根据安全管理器中配置的Realm、缓存管理器等组件来判断请求是否合法、用户是否具有访问权限等情况。
shiroFilterFactoryBean.setSecurityManager(getDefaultWebSecurityManager()); shiroFilterFactoryBean.setSecurityManager(getDefaultWebSecurityManager());
// 设置登录页面的URL当用户访问需要认证但未登录的资源时Shiro会自动重定向到该URL让用户进行登录操作这里设置为"/login",表示应用中对应的登录页面路径。
shiroFilterFactoryBean.setLoginUrl("/login"); shiroFilterFactoryBean.setLoginUrl("/login");
// 设置用户成功登录后的跳转URL当用户登录成功后Shiro会自动重定向到该URL这里设置为"/index",表示登录成功后默认跳转到应用中的首页路径(具体根据业务需求确定)。
shiroFilterFactoryBean.setSuccessUrl("/index"); shiroFilterFactoryBean.setSuccessUrl("/index");
// 将定义好的URL路径与对应的Shiro过滤器链定义的映射添加到ShiroFilterFactoryBean中例如将"/member/**"路径设置为需要进行认证("authc"表示需要进行身份认证的过滤器),
// 将"/**"路径设置为可以匿名访问("anon"表示允许匿名访问的过滤器通过这样的配置来精确控制不同URL资源的访问权限实现灵活的安全访问策略。
filterChainDefinitionMap.put("/member/**", "authc"); filterChainDefinitionMap.put("/member/**", "authc");
filterChainDefinitionMap.put("/**", "anon"); filterChainDefinitionMap.put("/**", "anon");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean; return shiroFilterFactoryBean;
} }
} }

@ -11,29 +11,76 @@ import org.thymeleaf.spring5.view.ThymeleafViewResolver;
import com.tamguo.common.utils.SystemConstant; import com.tamguo.common.utils.SystemConstant;
// 标识这是一个Spring的组件类用于配置Thymeleaf视图相关的一些静态变量通过实现EnvironmentAware接口可以获取Spring的应用程序环境信息
// 进而从环境配置中获取特定的属性值并将这些属性值以及一些常量值设置到ThymeleafViewResolver的静态变量中使得在Thymeleaf模板中可以方便地使用这些变量进行页面渲染等操作。
@Component @Component
public class ThymeleafConfig implements EnvironmentAware{ public class ThymeleafConfig implements EnvironmentAware {
// 通过@Resource注解注入Spring的Environment对象用于获取应用程序的配置属性信息例如从配置文件如application.properties或application.yml等中读取各种自定义的配置项值。
@Resource @Resource
private Environment env; private Environment env;
/**
* ThymeleafThymeleafViewResolver
* EnvironmentSystemConstant
* Thymeleaf使便
* ThymeleafViewResolverSpring使
*
* @param viewResolver ThymeleafViewResolver使Thymeleaf访使
*/
@Resource @Resource
private void configureThymeleafStaticVars(ThymeleafViewResolver viewResolver) { private void configureThymeleafStaticVars(ThymeleafViewResolver viewResolver) {
if(viewResolver != null) { if (viewResolver!= null) {
Map<String, Object> vars = new HashMap<>(); // 创建一个HashMap用于存储要设置的静态变量键为变量名值为对应的变量值后续会将这些变量设置到ThymeleafViewResolver中以便在Thymeleaf模板中使用。
vars.put("domainName", env.getProperty("domain.name")); Map<String, Object> vars = new HashMap<>();
vars.put("adminDomain", env.getProperty("admin.domain.name"));
vars.put("memberDomain", env.getProperty("member.domain.name")); // 从应用程序的配置属性中获取名为"domain.name"的属性值,并将其作为"domainName"变量的值存储到vars映射中
vars.put("tamguoDomain", env.getProperty("tamguo.domain.name")); // 该变量可能用于在Thymeleaf模板中生成完整的域名相关的链接、资源引用等情况例如拼接完整的图片地址、外部接口地址等具体根据业务需求确定。
vars.put("PAPER_TYPE_ZHENTI", SystemConstant.ZHENGTI_PAPER_ID); vars.put("domainName", env.getProperty("domain.name"));
vars.put("PAPER_TYPE_MONI", SystemConstant.MONI_PAPER_ID);
vars.put("PAPER_TYPE_YATI", SystemConstant.YATI_PAPER_ID); // 从应用程序的配置属性中获取名为"admin.domain.name"的属性值,并将其作为"adminDomain"变量的值存储到vars映射中
vars.put("PAPER_TYPE_MINGXIAO", SystemConstant.MINGXIAO_PAPER_ID); // 该变量可能用于在Thymeleaf模板中针对管理员相关的功能模块生成特定域名的链接、资源引用等情况例如管理员登录页面的域名、管理员后台接口的域名等具体根据业务中管理员模块的需求确定。
vars.put("BEIJING_AREA_ID", SystemConstant.BEIJING_AREA_ID); vars.put("adminDomain", env.getProperty("admin.domain.name"));
viewResolver.setStaticVariables(vars);
} // 从应用程序的配置属性中获取名为"member.domain.name"的属性值,并将其作为"memberDomain"变量的值存储到vars映射中
// 该变量可能用于在Thymeleaf模板中针对会员相关的功能模块生成特定域名的链接、资源引用等情况例如会员中心页面的域名、会员相关接口的域名等具体根据业务中会员模块的需求确定。
vars.put("memberDomain", env.getProperty("member.domain.name"));
// 从应用程序的配置属性中获取名为"tamguo.domain.name"的属性值,并将其作为"tamguoDomain"变量的值存储到vars映射中
// 该变量可能用于在Thymeleaf模板中针对特定业务域名以tamguo相关的业务场景生成链接、资源引用等情况具体根据业务中该域名对应的功能模块需求确定。
vars.put("tamguoDomain", env.getProperty("tamguo.domain.name"));
// 从自定义的SystemConstant类中获取名为ZHENGTI_PAPER_ID的常量值并将其作为"PAPER_TYPE_ZHENTI"变量的值存储到vars映射中
// 该变量可能用于在Thymeleaf模板中区分不同类型的试卷相关逻辑处理例如根据试卷类型显示不同的样式、进行不同的操作按钮显示等情况具体根据业务中试卷模块的需求确定。
vars.put("PAPER_TYPE_ZHENTI", SystemConstant.ZHENGTI_PAPER_ID);
// 从自定义的SystemConstant类中获取名为MONI_PAPER_ID的常量值并将其作为"PAPER_TYPE_MONI"变量的值存储到vars映射中
// 该变量可能用于在Thymeleaf模板中区分不同类型的试卷相关逻辑处理例如根据试卷类型显示不同的样式、进行不同的操作按钮显示等情况具体根据业务中试卷模块的需求确定。
vars.put("PAPER_TYPE_MONI", SystemConstant.MONI_PAPER_ID);
// 从自定义的SystemConstant类中获取名为YATI_PAPER_ID的常量值并将其作为"PAPER_TYPE_YATI"变量的值存储到vars映射中
// 该变量可能用于在Thymeleaf模板中区分不同类型的试卷相关逻辑处理例如根据试卷类型显示不同的样式、进行不同的操作按钮显示等情况具体根据业务中试卷模块的需求确定。
vars.put("PAPER_TYPE_YATI", SystemConstant.YATI_PAPER_ID);
// 从自定义的SystemConstant类中获取名为MINGXIAO_PAPER_ID的常量值并将其作为"PAPER_TYPE_MINGXIAO"变量的值存储到vars映射中
// 该变量可能用于在Thymeleaf模板中区分不同类型的试卷相关逻辑处理例如根据试卷类型显示不同的样式、进行不同的操作按钮显示等情况具体根据业务中试卷模块的需求确定。
vars.put("PAPER_TYPE_MINGXIAO", SystemConstant.MINGXIAO_PAPER_ID);
// 从自定义的SystemConstant类中获取名为BEIJING_AREA_ID的常量值并将其作为"BEIJING_AREA_ID"变量的值存储到vars映射中
// 该变量可能用于在Thymeleaf模板中涉及北京地区相关的逻辑处理例如根据地区ID进行特定地区的业务展示、数据筛选等情况具体根据业务中地区相关模块的需求确定。
vars.put("BEIJING_AREA_ID", SystemConstant.BEIJING_AREA_ID);
// 将包含所有静态变量的映射设置到ThymeleafViewResolver对象中使得这些变量可以在Thymeleaf模板中被访问和使用实现配置的生效方便页面渲染时的动态数据展示和逻辑处理。
viewResolver.setStaticVariables(vars);
}
} }
/**
* EnvironmentAwareEnvironmentSpring
* 便environmentenv便使
*
* @param environment Spring
*/
@Override @Override
public void setEnvironment(Environment environment) { public void setEnvironment(Environment environment) {
env = environment; env = environment;

@ -12,31 +12,69 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.tamguo.interceptor.MemberInterceptor; import com.tamguo.interceptor.MemberInterceptor;
// 标识这是一个Spring的配置类用于配置Spring Web应用相关的功能通过实现WebMvcConfigurer接口并重写其中的方法
// 可以对资源处理器、拦截器以及Cookie序列化等方面进行定制化配置以满足项目特定的业务需求例如设置文件资源的访问路径、添加会员相关请求的拦截逻辑、配置Cookie的相关属性等。
@Configuration @Configuration
public class WebConfig implements WebMvcConfigurer { public class WebConfig implements WebMvcConfigurer {
@Value("${file.storage.path}") // 通过@Value注解从配置文件如application.properties或application.yml等中注入文件存储路径属性值
private String fileStoragePath; // 该路径用于指定服务器上存储文件的实际位置后续在配置文件资源处理器时会用到以便正确地将特定URL路径映射到该文件存储位置实现文件的访问。
@Value("${cookie.domian.name}") @Value("${file.storage.path}")
private String cookieDomianName; private String fileStoragePath;
@Autowired
private MemberInterceptor memberInterceptor;
// 通过@Value注解从配置文件中注入Cookie的域名属性值该域名用于设置Cookie的作用域决定了Cookie在哪些域名下可以被发送和接收
// 在配置Cookie序列化相关逻辑时会使用该值来准确设置Cookie的域名属性确保会话相关的Cookie在正确的域名范围内有效。
@Value("${cookie.domian.name}")
private String cookieDomianName;
// 自动注入MemberInterceptor这是一个自定义的拦截器用于在特定的请求路径下执行一些前置处理逻辑比如可能用于验证会员的登录状态、权限等情况
// 在配置拦截器时会将该拦截器添加到相应的请求路径上,使其生效并发挥作用。
@Autowired
private MemberInterceptor memberInterceptor;
/**
* WebMvcConfigureraddResourceHandlersURL
* 使访URL
*
* @param registry URL
*/
@Override @Override
public void addResourceHandlers(ResourceHandlerRegistry registry) { public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/files/**").addResourceLocations("file:"+fileStoragePath); // 将以"/files/"开头的所有URL路径例如 "/files/image.jpg"、"/files/documents/report.pdf"等),
// 映射到服务器上以"file:"开头的实际文件存储路径通过fileStoragePath变量获取这样客户端访问 "/files/" 相关路径时,就能获取到存储在对应文件存储路径下的文件资源了。
registry.addResourceHandler("/files/**").addResourceLocations("file:" + fileStoragePath);
} }
@Override /**
public void addInterceptors(InterceptorRegistry registry) { * WebMvcConfigureraddInterceptors
registry.addInterceptor(memberInterceptor).addPathPatterns("/member/**"); * 使
} *
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 将自动注入的MemberInterceptor添加到以"/member/"开头的所有请求路径上(例如 "/member/profile"、"/member/orders"等),
// 意味着当客户端发起这些会员相关的请求时MemberInterceptor中的逻辑会先被执行进行相应的前置处理例如检查当前用户是否已登录为会员如果未登录则可能重定向到登录页面等操作。
registry.addInterceptor(memberInterceptor).addPathPatterns("/member/**");
}
/**
* CookieSerializerCookieBeanDefaultCookieSerializer
* Spring SessionCookieCookieCookie
* CookieCookie
*
* @return CookieSerializerSpringSpring SessionCookie使
*/
@Bean @Bean
public CookieSerializer defaultCookieSerializer(){ public CookieSerializer defaultCookieSerializer() {
DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer(); DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer();
// 设置Cookie的名称为"sessionId"这意味着在客户端和服务器之间传输的会话相关的Cookie名称将是"sessionId"
// 服务器通过该名称来识别和处理对应的会话信息,前端在发送请求时也会带上名为"sessionId"的Cookie来标识当前会话。
defaultCookieSerializer.setCookieName("sessionId"); defaultCookieSerializer.setCookieName("sessionId");
// 设置Cookie的域名使用之前注入的cookieDomianName属性值确定Cookie的作用域使得Cookie只会在指定的域名下有效
// 例如,如果域名设置为"example.com"那么只有访问该域名下的页面时相关的Cookie才会被发送和接收保证会话信息在正确的域名范围内传递。
defaultCookieSerializer.setDomainName(cookieDomianName); defaultCookieSerializer.setDomainName(cookieDomianName);
// 设置Cookie的路径为"/"表示该Cookie在整个域名下的所有路径都有效即无论访问域名下的哪个具体页面路径都会带上这个Cookie方便会话信息在整个应用的各个页面间共享和使用。
defaultCookieSerializer.setCookiePath("/"); defaultCookieSerializer.setCookiePath("/");
return defaultCookieSerializer; return defaultCookieSerializer;
} }

@ -8,48 +8,75 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
// 标识这是一个Spring的组件类用于拦截Web请求它继承自HandlerInterceptorAdapter通过重写其中的preHandle方法来实现自定义的请求拦截逻辑
// 主要用于检查用户是否已登录通过查看会话中是否存在特定的用户标识信息根据不同的请求类型如普通的GET请求、AJAX请求等以及登录状态进行相应的处理
// 例如未登录时对于AJAX请求返回特定的错误响应头和状态码对于普通GET请求则重定向到登录页面并携带当前请求的URL作为重定向后的参数对于其他非GET请求则直接重定向到登录页面。
@Component @Component
public class MemberInterceptor extends HandlerInterceptorAdapter{ public class MemberInterceptor extends HandlerInterceptorAdapter {
/** "重定向URL"参数名称 */ /**
* "重定向URL"
* URL便访
*/
private static final String REDIRECT_URL_PARAMETER_NAME = "redirectUrl"; private static final String REDIRECT_URL_PARAMETER_NAME = "redirectUrl";
/** 默认登录URL */ /**
* URL
* URLURL使便
*/
private static final String DEFAULT_LOGIN_URL = "/login.html"; private static final String DEFAULT_LOGIN_URL = "/login.html";
/** 登录URL */ /**
* URL
* URLURLDEFAULT_LOGIN_URL使
*/
private String loginUrl = DEFAULT_LOGIN_URL; private String loginUrl = DEFAULT_LOGIN_URL;
// 通过@Value注解从配置文件如application.properties或application.yml等中注入名为"tamguo.domain.name"的属性值,
// 该域名通常用于构建完整的登录页面URL等情况确保重定向到登录页面时使用的是正确的域名使得在不同的部署环境下域名可能不同能准确访问到登录页面。
@Value("${tamguo.domain.name}") @Value("${tamguo.domain.name}")
private String tamguoDomainName; private String tamguoDomainName;
/** /**
* *
* HandlerInterceptorAdapterpreHandle
* AJAXGET
* *
* @param request * @param request HttpServletRequestURL
* HttpServletRequest * @param response HttpServletResponse
* @param response * @param handler Controller使
* HttpServletResponse * @return trueControllerfalse
* @param handler * @throws Exception
*
* @return
*/ */
@Override @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object currMember = request.getSession().getAttribute("currMember"); // 从当前请求的会话Session中获取名为"currMember"的属性值,该属性通常在用户登录成功后被设置,用于标识当前登录的用户信息,
if (currMember != null) { // 如果能获取到该属性值即不为null表示用户已经登录此时允许请求继续执行后续的处理流程直接返回true。
Object currMember = request.getSession().getAttribute("currMember");
if (currMember!= null) {
return true; return true;
} else { } else {
// 获取请求头中名为"X-Requested-With"的字段值该字段常用于判断请求是否为AJAX请求不同的前端框架在发起AJAX请求时通常会设置这个请求头字段方便后端进行区分处理。
String requestType = request.getHeader("X-Requested-With"); String requestType = request.getHeader("X-Requested-With");
if (requestType != null && requestType.equalsIgnoreCase("XMLHttpRequest")) { if (requestType!= null && requestType.equalsIgnoreCase("XMLHttpRequest")) {
// 如果请求头中的"X-Requested-With"字段值表明这是一个AJAX请求且用户未登录那么向响应头中添加一个名为"loginStatus"的字段,值为"accessDenied"
// 用于告知前端该请求因为未登录被拒绝访问然后通过response发送一个HTTP状态码为FORBIDDEN403表示禁止访问的错误响应给客户端最后返回false拦截该请求中断后续的请求处理流程。
response.addHeader("loginStatus", "accessDenied"); response.addHeader("loginStatus", "accessDenied");
response.sendError(HttpServletResponse.SC_FORBIDDEN); response.sendError(HttpServletResponse.SC_FORBIDDEN);
return false; return false;
} else { } else {
// 如果不是AJAX请求则根据请求的方法GET、POST等进行不同的重定向处理。
if (request.getMethod().equalsIgnoreCase("GET")) { if (request.getMethod().equalsIgnoreCase("GET")) {
String redirectUrl = request.getQueryString() != null ? request.getRequestURI() + "?" + request.getQueryString() : request.getRequestURI(); // 如果是GET请求构建要重定向的URL参数部分先判断请求是否带有查询字符串即请求URL中是否包含 "?" 后面的参数部分),
// 如果有查询字符串则将请求的URI不包含域名和端口部分的请求路径加上原有的查询字符串作为重定向的URL参数部分如果没有查询字符串则只使用请求的URI作为重定向的URL参数部分
// 这样做是为了在用户登录成功后能根据这个参数值将用户重定向回原本想要访问的页面。
String redirectUrl = request.getQueryString()!= null? request.getRequestURI() + "?" + request.getQueryString() : request.getRequestURI();
// 进行重定向操作将用户重定向到登录页面构建完整的登录页面URL通过将配置的域名tamguoDomainName、登录页面的URL路径loginUrl以及重定向URL参数使用URLEncoder进行UTF-8编码确保特殊字符能正确传递拼接起来
// 形成最终的重定向地址发送给客户端让用户先进行登录操作然后返回false拦截该请求中断后续的请求处理流程。
response.sendRedirect(tamguoDomainName + loginUrl + "?" + REDIRECT_URL_PARAMETER_NAME + "=" + URLEncoder.encode(redirectUrl, "UTF-8")); response.sendRedirect(tamguoDomainName + loginUrl + "?" + REDIRECT_URL_PARAMETER_NAME + "=" + URLEncoder.encode(redirectUrl, "UTF-8"));
} else { } else {
// 如果是除GET之外的其他请求方法如POST等直接将用户重定向到登录页面构建完整的登录页面URL通过将配置的域名tamguoDomainName和登录页面的URL路径loginUrl拼接起来
// 形成最终的重定向地址发送给客户端让用户先进行登录操作然后返回false拦截该请求中断后续的请求处理流程。
response.sendRedirect(tamguoDomainName + loginUrl); response.sendRedirect(tamguoDomainName + loginUrl);
} }
return false; return false;

@ -5,30 +5,58 @@ import java.io.IOException;
import java.security.MessageDigest; import java.security.MessageDigest;
import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.binary.Hex;
// 该类是一个工具类主要提供了计算文件MD5值的功能通过读取文件内容并按照一定的算法逐步更新MD5摘要信息最终生成文件的MD5值
// 并且能够处理大文件(通过分块读取文件内容的方式避免一次性将整个大文件读入内存),同时对可能出现的异常进行了相应处理,并确保文件输入流资源在使用后能正确关闭。
public class FileMd5Utils { public class FileMd5Utils {
/** /**
* md5() * md5
* @return md5 value * MD58192
* MD5MD5MD5
* nullMD5
*
* @param fileInputStream MD5MD5
*
* @return MD5nullMD5
*/ */
public static String getMD5(FileInputStream fileInputStream) { public static String getMD5(FileInputStream fileInputStream) {
try { try {
// 获取MD5算法的MessageDigest实例用于计算文件内容的MD5摘要信息MessageDigest类提供了按照特定算法这里是MD5算法计算数据摘要的功能
// 通过调用getInstance方法并传入算法名称"MD5"来创建对应的实例后续可以使用该实例不断更新要计算摘要的数据内容最终生成完整的摘要结果即MD5值对应的字节数组
MessageDigest MD5 = MessageDigest.getInstance("MD5"); MessageDigest MD5 = MessageDigest.getInstance("MD5");
// 创建一个字节数组缓冲区用于每次从文件输入流中读取文件内容的数据块这里设置缓冲区大小为8192字节通过分块读取文件内容的方式
// 可以避免一次性将整个大文件读入内存从而能够处理大文件的MD5计算提高内存使用效率以及避免内存溢出等问题特别是对于大型文件的处理更为合理和可行。
byte[] buffer = new byte[8192]; byte[] buffer = new byte[8192];
// 用于记录每次从文件输入流中实际读取到的字节数,在循环读取文件内容过程中,每次读取操作都会更新该变量的值,通过判断其是否为 -1 来确定是否已经读取完整个文件内容,
// 如果读取到文件末尾则返回 -1表示文件内容已全部读完循环结束。
int length; int length;
while ((length = fileInputStream.read(buffer)) != -1) {
// 通过循环不断从文件输入流中读取文件内容到缓冲区每次读取的数据长度存储在length变量中然后使用MD5摘要对象的update方法将读取到的数据块更新到摘要计算中
// 这样不断更新摘要信息直到读取完整个文件内容即length为 -1 时结束循环实现对整个文件内容的MD5摘要计算。
while ((length = fileInputStream.read(buffer))!= -1) {
MD5.update(buffer, 0, length); MD5.update(buffer, 0, length);
} }
// 通过Hex类来自Apache Commons Codec库的encodeHex方法将最终生成的MD5摘要字节数组转换为十六进制字符串形式
// 这样得到的就是常见的以十六进制字符串表示的文件MD5值方便后续存储、展示以及与其他MD5值进行比对等操作最后将该十六进制字符串作为文件的MD5值返回。
return new String(Hex.encodeHex(MD5.digest())); return new String(Hex.encodeHex(MD5.digest()));
} catch (Exception e) { } catch (Exception e) {
// 如果在获取MD5算法实例、读取文件内容、更新MD5摘要或者转换MD5摘要字节数组为十六进制字符串等过程中出现异常
// 则会打印异常的堆栈信息通过e.printStackTrace()方法方便后续排查问题同时返回null表示获取文件MD5值失败调用者可根据返回值进行相应的错误处理。
e.printStackTrace(); e.printStackTrace();
return null; return null;
} finally { } finally {
try { try {
if (fileInputStream != null){ // 在finally块中确保文件输入流资源能够正确关闭避免因异常等情况导致资源未释放的问题先判断文件输入流是否不为空
// 如果不为空则调用其close方法关闭输入流释放相关的文件资源确保文件读取操作完成后正确关闭资源避免资源泄漏等问题。
if (fileInputStream!= null) {
fileInputStream.close(); fileInputStream.close();
} }
} catch (IOException e) { } catch (IOException e) {
// 如果在关闭文件输入流过程中出现异常(例如文件被其他程序占用无法正常关闭等情况),同样会打印异常的堆栈信息,方便后续查看问题原因,
// 虽然此处无法对关闭流失败进行更多的补救措施,但通过打印异常信息可以帮助了解资源释放出现的问题。
e.printStackTrace(); e.printStackTrace();
} }
} }

@ -6,36 +6,94 @@ import org.apache.shiro.subject.Subject;
import com.tamguo.modules.member.model.MemberEntity; import com.tamguo.modules.member.model.MemberEntity;
// 该类是一个工具类主要提供了一系列基于Shiro框架的便捷方法用于获取Shiro相关的核心对象如Subject、Session等、获取当前登录用户的信息如用户实体、用户ID等、操作会话属性设置和获取以及判断用户是否登录、执行用户登出等功能
// 在整个项目中方便其他地方复用这些与Shiro框架交互的常用操作逻辑避免重复编写相似的代码来获取Shiro相关对象和信息。
public class ShiroUtils { public class ShiroUtils {
/**
* ShiroSession
* ShiroSecurityUtilsgetSubjectSubjectSubject
* 便
*
* @return ShiroSession
*/
public static Session getSession() { public static Session getSession() {
return SecurityUtils.getSubject().getSession(); return SecurityUtils.getSubject().getSession();
} }
/**
* ShiroSubject
* ShiroSubject
* ShiroSecurityUtilsgetSubjectShiro
*
* @return Subject
*/
public static Subject getSubject() { public static Subject getSubject() {
return SecurityUtils.getSubject(); return SecurityUtils.getSubject();
} }
/**
* MemberEntity
* ShiroSecurityUtilsSubjectSubjectPrincipalMemberEntity
* ID便使
*
* @return MemberEntitynull使
*/
public static MemberEntity getMember() { public static MemberEntity getMember() {
return (MemberEntity)SecurityUtils.getSubject().getPrincipal(); return (MemberEntity) SecurityUtils.getSubject().getPrincipal();
} }
/**
* ID
* getMemberMemberEntityID便
* IDID便
*
* @return IDgetMemberMemberEntity使
*/
public static String getMemberId() { public static String getMemberId() {
return getMember().getId(); return getMember().getId();
} }
/**
* ShiroSession
* getSession使setAttributekeyvalue
* 便
*
* @param key ObjectJava
* @param value 便使Object
*/
public static void setSessionAttribute(Object key, Object value) { public static void setSessionAttribute(Object key, Object value) {
getSession().setAttribute(key, value); getSession().setAttribute(key, value);
} }
/**
* ShiroSession
* getSession使getAttributekey
* null便使
*
* @param key Object使
* @return nullObject使
*/
public static Object getSessionAttribute(Object key) { public static Object getSessionAttribute(Object key) {
return getSession().getAttribute(key); return getSession().getAttribute(key);
} }
/**
*
* ShiroSecurityUtilsSubjectPrincipalnullnull
* 便
*
* @return SubjectPrincipalnulltruefalse
*/
public static boolean isLogin() { public static boolean isLogin() {
return SecurityUtils.getSubject().getPrincipal() != null; return SecurityUtils.getSubject().getPrincipal()!= null;
} }
/**
*
* ShiroSecurityUtilsSubjectlogout
* 使
*/
public static void logout() { public static void logout() {
SecurityUtils.getSubject().logout(); SecurityUtils.getSubject().logout();
} }

Loading…
Cancel
Save