SpringBoot整合Shiro及JWT

128 阅读1分钟

步骤1:创建Spring Boot项目 首先,使用Spring Initializr创建一个新的Spring Boot项目。您可以选择适合您的构建工具(如Maven或Gradle)和Java版本。确保在项目依赖中包含Shiro和JWT相关的库。

步骤2:配置Shiro 在项目中创建一个Shiro配置类,用于配置Shiro的安全策略、Realm和会话管理等。您可以使用注解或编程方式进行配置。以下是一个示例配置类的代码:

@Configuration
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        filterFactoryBean.setSecurityManager(securityManager);
        // 设置登录url
        filterFactoryBean.setLoginUrl("/login");
        // 设置未授权url
        filterFactoryBean.setUnauthorizedUrl("/unauthorized");

        // 定义过滤器链
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 公开访问的资源
        filterChainDefinitionMap.put("/public/**", "anon");
        // 需要身份认证的资源
        filterChainDefinitionMap.put("/private/**", "authc");
        filterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return filterFactoryBean;
    }

    @Bean
    public DefaultWebSecurityManager securityManager(Realm realm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;
    }

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

步骤3:实现自定义Realm 创建一个自定义的Realm类,用于处理身份认证和授权逻辑。您可以继承AuthorizingRealm类并实现其中的方法。以下是一个示例Realm类的代码:

public class MyRealm extends AuthorizingRealm {

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 实现授权逻辑
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        // 添加角色和权限
        authorizationInfo.addRole("admin");
        authorizationInfo.addStringPermission("user:read");
        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 实现身份认证逻辑
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        String username = usernamePasswordToken.getUsername();
        String password = new String(usernamePasswordToken.getPassword());

        // 根据用户名查询用户信息
        User user = userService.findByUsername(username);

        if (user == null || !password.equals(user.getPassword())) {
            throw new AuthenticationException("用户名或密码错误");
        }

        return new SimpleAuthenticationInfo(username, password, getName());
    }
}

步骤4:实现登录接口 创建一个登录接口,用于接收用户的用户名和密码,并使用Shiro进行身份认证。如果认证成功,生成JWT并返回给客户端。以下是一个示例登录接口的代码:

@RestController
public class LoginController {

    @PostMapping("/login")
    public String login(@RequestBody LoginRequest loginRequest) {
        Subject currentUser = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(loginRequest.getUsername(), loginRequest.getPassword());

        try {
            currentUser.login(token);
        } catch (AuthenticationException e) {
            return "登录失败:" + e.getMessage();
        }

        // 生成JWT并返回给客户端
        String jwt = JwtUtils.generateToken(loginRequest.getUsername());
        return "登录成功,JWT:" + jwt;
    }
}

步骤5:实现JWT工具类 创建一个JWT工具类,用于生成和验证JWT。您可以使用现有的JWT库(如jjwt)来简化操作。以下是一个示例JWT工具类的代码:

public class JwtUtils {

    private static final String SECRET_KEY = "your-secret-key";
    private static final long EXPIRATION_TIME = 86400000; // 24小时

    public static String generateToken(String username) {
        Date now = new Date();
        Date expirationDate = new Date(now.getTime() + EXPIRATION_TIME);

        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(now)
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS512, SECRET_KEY)
                .compact();
    }

    public static boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
            return true;
        } catch (JwtException | IllegalArgumentException e) {
            return false;
        }
    }

    public static String getUsernameFromToken(String token) {
        Claims claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
        return claims.getSubject();
    }
}

步骤6:实现鉴权过滤器 创建一个鉴权过滤器,用于在每个请求中验证用户的角色和权限。如果用户没有足够的权限,返回相应的错误信息。以下是一个示例鉴权过滤器的代码:

public class AuthorizationFilter extends AuthorizationFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        Subject subject = getSubject(request, response);
        String[] rolesArray = (String[]) mappedValue;

        if (rolesArray == null || rolesArray.length == 0) {
            return true;
        }

        for (String role : rolesArray) {
            if (subject.hasRole(role)) {
                return true;
            }
        }

        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        httpServletResponse.getWriter().write("无权限访问");
        return false;
    }
}

步骤7:配置过滤器链 在Shiro配置类中配置过滤器链,以定义URL的访问权限。以下是一个示例过滤器链的配置代码:

@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
    ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
    filterFactoryBean.setSecurityManager(securityManager);
    // 设置登录url
    filterFactoryBean.setLoginUrl("/login");
    // 设置未授权url
    filterFactoryBean.setUnauthorizedUrl("/unauthorized");

    // 定义过滤器链
    Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
    // 公开访问的资源
    filterChainDefinitionMap.put("/public/**", "anon");
    // 需要身份认证的资源
    filterChainDefinitionMap.put("/private/**", "authc");
    filterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

    return filterFactoryBean;
}

步骤8:测试与部署 完成开发后,您可以编写单元测试来验证代码是否按预期工作。您可以使用JUnit和Mockito等测试框架来编写测试用例。完成测试后,您可以将应用程序部署到生产环境中。