Spring+Shiro+MybatisPlus 导致事务失效

最近在使用Shiro做权限管理的时候,在继承了AuthorizingRealm类的UserRealm自动注入了用户服务的接口ISysUserService。

@Slf4j
public class UserRealm extends AuthorizingRealm {
    @Autowired
    ISysUserService sysUserService;

    @Autowired
    ISysRoleService sysRoleService;
    }

在启动时发现,在Info日志中总是出现

2020-06-04 15:19:31.789 INFO 24676 — [ main] trationDelegate$BeanPostProcessorChecker : Bean ‘sysUserServiceImpl’ of type [org.springframework.aop.scope.ScopedProxyFactoryBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

显示ISysUserService 接口的实现类sysUserServiceImpl的后置处理器并没有完全使用,在网上找到了这一篇文章,有兴趣的小伙伴可以看一下。
【小家Spring】Spring IOC容器启动流程 AbstractApplicationContext#refresh()方法源码分析(一)
问题大致呢,是由于在UserRealm中采取自动注入的方式注入了ISysUserService的实体Bean,由于Shiro执行链的原因,会导致ISysUserService的实体Bean提前实例化完成并启动,导致后续的BeanPostProcessor接口并没有把该Bean处理完成

@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUserEntity> implements ISysUserService {

    @Autowired
    public ISysuserRoleService iSysuserRoleService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean removeUserAndRolesByUserId(String id) {
        //删除用户
        QueryWrapper<SysUserEntity> sysUserEntityQueryWrapper = new QueryWrapper<>();
        sysUserEntityQueryWrapper.eq("id",id);
        this.getBaseMapper().delete(sysUserEntityQueryWrapper);
        //删除用户---角色
        QueryWrapper<SysuserRoleEntity> sysuserRoleEntityQueryWrapper = new QueryWrapper<>();
        sysuserRoleEntityQueryWrapper.eq("UserId",id);
        iSysuserRoleService.remove(sysuserRoleEntityQueryWrapper);
        return true;
    }
}

因为我的用户服务类中开启了事务,所以就实体Bean的提前初始化完成就造成了没有生成事务的代理对象。
debug可以看到在该类的调用栈中,从Controller直接调用到了Service的实体类,而并不是通过事务的代理类来进行调用,所以说就没有生成这个类的事务代理对象,那么事务的使用肯定是失败的。

解决方法

  1. 我的解决方法是在该类的上方添加了@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUserEntity> implements ISysUserService {

    @Autowired
    public ISysuserRoleService iSysuserRoleService;

强制生成一个Cglib代理对象,再次启动查看该类的调用栈
在这里插入图片描述可以很清楚的看到该服务的调用由事务的Cglib代理对象发起调用,此时事务就开启成功了。

  1. 另外也可以将UserRealm中的自动注入改为懒加载,就可以当ISysUserService整个初始化流程结束后再注入UserRealm中,避免了Bean的提前启动。
@Slf4j
public class UserRealm extends AuthorizingRealm {
    @Autowired
    @Lazy
    ISysUserService sysUserService;

    @Autowired
    ISysRoleService sysRoleService;
    }

如果不是因为Shiro引起的事务失效的问题并且类之间的依赖关系比较难以整理,大家可以尝试用第一种方法。

发生这样的问题主要还是不清楚Bean的初始化流程和Bean相互之间的依赖调用关系,要补。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
这个框架整合可以分为以下几个步骤: 1. 集成 Spring Boot:在 pom.xml 文件中添加 Spring Boot 依赖,创建 Spring Boot 启动类。 2. 集成 MyBatis Plus:在 pom.xml 文件中添加 MyBatis Plus 依赖,配置数据源和 MyBatis Plus 相关配置。 3. 集成 Shiro:在 pom.xml 文件中添加 Shiro 依赖,创建 Shiro 配置类,配置 Shiro 的 Realm 和 SecurityManager。 4. 集成 JWT:在 pom.xml 文件中添加 jjwt 依赖,创建 JWT 工具类,用于生成和解析 JWT。 下面是一个简单的示例代码,你可以根据你的实际需求进行调整: 1. pom.xml 文件中添加依赖: ```xml <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MyBatis Plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus.version}</version> </dependency> <!-- Shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency> <!-- jjwt --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>${jjwt.version}</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>${jjwt.version}</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>${jjwt.version}</version> <scope>runtime</scope> </dependency> ``` 2. 创建 Spring Boot 启动类: ```java @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 3. 配置 MyBatis Plus: ```java @Configuration @MapperScan("com.example.mapper") public class MyBatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } } ``` 4. 配置 Shiro: ```java @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); shiroFilter.setSecurityManager(securityManager); shiroFilter.setUnauthorizedUrl("/401"); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/401", "anon"); filterChainDefinitionMap.put("/**", "jwt"); shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap); Map<String, Filter> filters = new LinkedHashMap<>(); filters.put("jwt", new JwtFilter()); shiroFilter.setFilters(filters); return shiroFilter; } @Bean public DefaultWebSecurityManager securityManager(Realm realm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm); return securityManager; } @Bean public Realm realm() { return new UserRealm(); } } ``` 5. 创建 JWT 工具类: ```java public class JwtUtil { private static final String SECRET_KEY = "your_secret_key"; private static final long EXPIRATION_TIME = 3600_000; // 1 hour public static String generateToken(String username) { Date now = new Date(); Date expiration = new Date(now.getTime() + EXPIRATION_TIME); return Jwts.builder() .setSubject(username) .setIssuedAt(now) .setExpiration(expiration) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .compact(); } public static String getUsernameFromToken(String token) { Claims claims = Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody(); return claims.getSubject(); } public static boolean validateToken(String token) { try { Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token); return true; } catch (JwtException e) { return false; } } } ``` 6. 创建 JwtFilter: ```java public class JwtFilter extends AuthenticatingFilter { @Override protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) { HttpServletRequest request = (HttpServletRequest) servletRequest; String token = request.getHeader("Authorization"); if (StringUtils.isBlank(token)) { return null; } return new JwtToken(token); } @Override protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { HttpServletResponse response = (HttpServletResponse) servletResponse; response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.getWriter().write("{\"code\":401,\"message\":\"未登录或登录已过期,请重新登录\"}"); return false; } @Override protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception { HttpServletRequest request = (HttpServletRequest) servletRequest; String token = request.getHeader("Authorization"); if (StringUtils.isBlank(token)) { return false; } return JwtUtil.validateToken(token); } } ``` 7. 创建 JwtToken: ```java public class JwtToken implements AuthenticationToken { private final String token; public JwtToken(String token) { this.token = token; } @Override public Object getPrincipal() { return JwtUtil.getUsernameFromToken(token); } @Override public Object getCredentials() { return token; } } ``` 8. 创建 UserRealm: ```java public class UserRealm extends AuthorizingRealm { @Autowired private UserService userService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { String username = (String) authenticationToken.getPrincipal(); User user = userService.getByUsername(username); if (user == null) { throw new UnknownAccountException("账号不存在"); } return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName()); } } ``` 这样,简单的 Spring Boot + Shiro + JWT + MyBatis Plus 整合就完成了。你可以根据具体的需求,对代码进行修改和优化。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值