springboot和shiro的整合

直接贴上代码

1. 所需要的jar包

复制代码
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.0</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.4.0</version>
        </dependency>
复制代码

2. 所需要的Configuration

复制代码
/**
 * Shiro的配置文件
 */
@Configuration
public class ShiroConfig {
    /**
     * 自定义Realm,可以多个
     */
    @Bean
    public ShiroRealm myRealm(){
        ShiroRealm shiroRealm = new ShiroRealm();
        return shiroRealm;
    }
    @Bean
    public SessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        // 设置session过期时间为30分钟
        sessionManager.setGlobalSessionTimeout(30 * 60 * 1000);
        // 设置定时器,检查session时候国企
        sessionManager.setSessionValidationSchedulerEnabled(true);
        // 去掉shiro登录时url里的JSESSIONID
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }

    /**
     * SecurityManager 安全管理器;Shiro的核心
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        //将session设置到SecurityManager中
        manager.setSessionManager(sessionManager());
        //将realm设置到SecurityManager中
        manager.setRealm(myRealm());
        return manager;
    }

    /**
     * shiroFilter过滤类
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager());
        bean.setLoginUrl("/");           // 登录的路径
        bean.setSuccessUrl("/success"); // 登录成功后跳转的路径
        bean.setUnauthorizedUrl("/403"); // 验证失败后跳转的路径
        Map<String, String> map = new LinkedHashMap<>();

        map.put("/captcha", "anon");
        map.put("/captcha.jpg", "anon");
        map.put("/statics/**", "anon");
        map.put("/login.html", "anon");
        map.put("/favicon.ico", "anon");
        map.put("/login", "anon");
        map.put("/**", "authc");
        map.put("/logout", "logout");
        bean.setFilterChainDefinitionMap(map);
        return bean;
    }

    /**
     * 配置Shiro生命周期处理器
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 自动创建代理类,若不添加,Shiro的注解可能不会生效。
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 开启Shiro的注解
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }
    /**
     * 配置加密匹配,使用MD5的方式,进行1024次加密
     */
//    @Bean
//    public HashedCredentialsMatcher hashedCredentialsMatcher() {
//        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//        hashedCredentialsMatcher.setHashAlgorithmName("des");
//        hashedCredentialsMatcher.setHashIterations(1024);
//        return hashedCredentialsMatcher;
//    }
}
复制代码

3.重写realm

复制代码
public class ShiroRealm extends AuthorizingRealm {
    @Autowired
    private LoginDao loginDao;

    // 验证权限(登录的时候使用的)
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();
        UserDO userDO = loginDao.selectOne(new QueryWrapper<UserDO>().eq("username", username));
        if (userDO == null) {
            throw new UnknownAccountException("未知账号");//没找到帐号
        }
        // 交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配
        //使用 ByteSource.Util.bytes() 来计算盐值.
        return new SimpleAuthenticationInfo(userDO, userDO.getPassword(),ByteSource.Util.bytes(userDO.getPassword()), getName());
    }

    // 添加权限
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    /**
     * 设定Password校验
     */
    @PostConstruct
    public void initCredentialsMatcher() {
        //重写shiro的密码验证,让shiro用我自己的验证
        setCredentialsMatcher(new CustomCredentialsMatcher());
    }
}
复制代码

4. 重写密码验证类

    

复制代码
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {

    /**
     * 此方法为HashedCredentialsMatcher类中真正验证密码是否一致的地方
     */
    @Override
    public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) {
        // 明明authcToken跟info两个对象的里的Credentials类型都是Object,断点看到的类型都是 char[]
        // 但是 token里转成String要先强转成 char[]
        // 而info里取Credentials就可以直接使用 String.valueOf() 转成String
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        Object tokenCredentials = encrypt(String.valueOf(token.getPassword()));
        Object infocredentials = getCredentials(info);
        return equals(tokenCredentials, infocredentials);
    }

    /**
     * 明文密码加密,将传进来密码加密方法
     */
    public String encrypt(String password) {
        //这里可以选择自己的密码验证方式 比如 md5或者sha256等
        String passwordEncrypt = password
        return passwordEncrypt;
    }
}
复制代码

5. 登录的Controller

复制代码
public userDO login(@RequestBody LoginRequest loginRequest) {
        try {
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(loginRequest.getUsername(), loginRequest.getPassword());
            subject.login(token);
            System.out.println("登录成功");
        } catch (UnknownAccountException e) {
            return HttpResultUtil.error("账号不存在");
        } catch (IncorrectCredentialsException e) {
            return HttpResultUtil.error("密码错误");
        }
        catch (AuthenticationException e) {
            return HttpResultUtil.error("账户被禁用,无法登录,请联系管理员");
        }
        UserDO userDO = (UserDO) SecurityUtils.getSubject().getPrincipal();
        return userDO
}
复制代码

 

posted @   这都没什么  阅读(245)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示