返回

技术指南:用SpringBoot整合Shiro(后端)

后端

SpringBoot整合Shiro(后端)技术指南

本指南将指导您将SpringBoot与Shiro集成,并在前后端分离模式下实现JWT认证。通过重写Shiro的过滤器,您可以自定义错误返回,避免重定向到登录页面,从而实现前后端分离的无缝衔接。

为什么要集成SpringBoot和Shiro?

SpringBoot是一个流行的Java框架,它简化了Spring应用的配置和开发。Shiro是一个功能强大的安全框架,它提供了灵活的认证和授权机制。将SpringBoot与Shiro集成,可以为您的应用提供强大的安全保障,并简化安全配置和管理。

集成步骤

  1. 添加SpringBoot和Shiro依赖
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring-boot-starter</artifactId>
        <version>1.4.0</version>
    </dependency>
</dependencies>
  1. 配置Shiro

application.yml文件中配置Shiro:

shiro:
  web:
    enabled: true
  jwt:
    secret: my-secret-key
  1. 创建自定义Realm

自定义Realm用于验证用户身份和授权。创建一个实现org.apache.shiro.realm.Realm接口的类,并重写doGetAuthenticationInfodoGetAuthorizationInfo方法。

public class MyRealm implements Realm {

    @Override
    public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
        // 从token中获取用户名和密码
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());

        // 根据用户名从数据库中查询用户
        User user = userService.findByUsername(username);

        // 如果用户存在并密码正确,则返回AuthenticationInfo
        if (user != null && user.getPassword().equals(password)) {
            return new SimpleAuthenticationInfo(username, password, getName());
        }

        // 如果用户不存在或密码不正确,则抛出异常
        throw new UnknownAccountException("用户不存在或密码不正确");
    }

    @Override
    public AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 从principals中获取用户名
        String username = (String) principals.getPrimaryPrincipal();

        // 根据用户名从数据库中查询用户的角色和权限
        Set<String> roles = userService.findRolesByUsername(username);
        Set<String> permissions = userService.findPermissionsByUsername(username);

        // 返回AuthorizationInfo
        return new SimpleAuthorizationInfo(roles, permissions);
    }
}
  1. 配置自定义Realm

ShiroConfig类中配置自定义Realm:

@Configuration
public class ShiroConfig {

    @Bean
    public Realm realm() {
        return new MyRealm();
    }
}
  1. 重写Shiro的过滤器

为了实现前后端分离,我们需要重写Shiro的过滤器,以便在用户未认证时返回JSON格式的错误信息,而不是重定向到登录页面。

public class JwtFilter extends PathMatchingFilter {

    @Override
    protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        // 从请求头中获取JWT token
        String token = request.getHeader("Authorization");

        // 如果token为空,则返回JSON格式的错误信息
        if (token == null) {
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(new ObjectMapper().writeValueAsString(new ResponseResult(401, "Unauthorized")));
            return false;
        }

        // 如果token不为空,则解析JWT token
        try {
            Jwts.parser().setSigningKey(ShiroConfig.SECRET_KEY).parseClaimsJws(token);
        } catch (Exception e) {
            // 如果解析JWT token失败,则返回JSON格式的错误信息
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(new ObjectMapper().writeValueAsString(new ResponseResult(401, "Unauthorized")));
            return false;
        }

        // 如果解析JWT token成功,则继续执行请求
        return true;
    }
}
  1. 注册JwtFilter

ShiroConfig类中注册JwtFilter:

@Configuration
public class ShiroConfig {

    @Bean
    public Realm realm() {
        return new MyRealm();
    }

    @Bean
    public JwtFilter jwtFilter() {
        return new JwtFilter();
    }

    @Bean
    public DefaultFilterInvocationDefinitionMap filterInvocationDefinitionMap() {
        DefaultFilterInvocationDefinitionMap definitionMap = new DefaultFilterInvocationDefinitionMap();
        definitionMap.put("/**", "jwtFilter");
        return definitionMap;
    }
}

总结

通过以上步骤,您就可以将SpringBoot与Shiro集成,并在前后端分离模式下实现JWT认证。通过重写Shiro的过滤器,您可以自定义错误返回,避免重定向到登录页面,从而实现前后端分离的无缝衔接。