SpringBoot整合Shiro实现用户认证

简介: SpringBoot整合Shiro实现用户认证

前言

大家好,一直以来我都本着用最通俗的话理解核心的知识点, 我认为所有的难点都离不开 基础知识 的铺垫。目前正在出一个SpringBoot长期系列教程,从入门到进阶, 篇幅会较多~


适合人群

  • 学完Java基础
  • 想通过Java快速构建web应用程序
  • 想学习或了解SpringBoot
  • SpringBoot进阶学习

大佬可以绕过 ~


背景

如果你是一路看过来的,很高兴你能够耐心看完。之前带大家学了Springboot基础部分,对基本的使用有了初步的认识, 接下来的几期内容将会带大家进阶使用,会先讲解基础中间件的使用和一些场景的应用,或许这些技术你听说过,没看过也没关系,我会带大家一步一步的入门,耐心看完你一定会有收获~


情景回顾

上期带大家学习了Shiro安全框架的基本概念和工作原理, 相信有了基本的认识之后,下面学习将会容易些,本期将带大家学习整合Shiro框架和基本使用。同样的,我们集成到Springboot中。


项目源码(持续更新 欢迎star⭐️)


环境搭建

我们先修改pom.xml, 引入相关依赖, 为了后期方便学习,我对项目结构做了一些调整,在目录下新建一个模块用于Shiro学习, 具体大家可以查看源码:

<!-- shiro-spring -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>
复制代码


配置 Shiro

今天带大家实现一个用户认证的功能, 我们先添加配置ShiroConfig:

@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 设置securityManager
        shiroFilterFactoryBean.setSecurityManager((SecurityManager) securityManager);
        // 登录的url
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后跳转的url
        shiroFilterFactoryBean.setSuccessUrl("/index");
        // 未授权url
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 定义filterChain,静态资源不拦截
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/fonts/**", "anon");
        filterChainDefinitionMap.put("/img/**", "anon");
        // druid数据源监控页面不拦截
        filterChainDefinitionMap.put("/druid/**", "anon");
        // 配置退出过滤器,其中具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/", "anon");
        // 除上以外所有url都必须认证通过才可以访问,未通过认证自动访问LoginUrl
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    @Bean
    public DefaultWebSecurityManager securityManager(){
        // 配置SecurityManager,并注入shiroRealm
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        securityManager.setRealm(shiroRealm());
        return securityManager;
    }
    /**
     * 自定义实现的Realm
     * @return
     */
    @Bean
    public CustomRealm shiroRealm(){
        CustomRealm shiroRealm = new CustomRealm();
        return shiroRealm;
    }
}
复制代码


简单说下, anon代表的是允许资源公开访问, authc代表的是资源需要通过认证后才能访问。其实大部分都已经贴在注释上了,大家跟着配就好,重点关注的是shiroRealm()这个方法, 里边有一个类需要我们自行实现,下边我就带大家实现一下:

public class CustomRealm extends AuthorizingRealm {
    /**
     * 获取用户角色和权限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        return null;
    }
    /**
     * 登录认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 获取用户输入的用户名和密码
        String userName = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());
        System.out.println("用户" + userName + "认证-----ShiroRealm.doGetAuthenticationInfo");
        // 通过用户名到数据库查询用户信息
        User user = UserMock.getUserByUsername(userName);
        if (user == null) {
            throw new UnknownAccountException("用户名或密码错误!");
        }
        if (!password.equals(user.getPassword())) {
            throw new IncorrectCredentialsException("用户名或密码错误!");
        }
        if (user.getState().equals("0")) {
            throw new LockedAccountException("账号已被锁定,请联系管理员!");
        }
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
        return info;
    }
}
复制代码


这里为了方便演示,我直接写死到对象里,使用测试数据来模拟数据库查询到的用户信息,真实业务中是要去数据库查询用户信息的:

@Data
public class User {
    public Integer id;
    public String username;
    public String password;
    public String role;
    public String state;
}
复制代码


定义一个 UserMock

public class UserMock {
    public static String getPassword(String username) {
        System.out.println("用户名:   " + username);
        if(username.equals("admin")) {
            return "123";
        }else {
            return "456";
        }
    }
    public static String getRole(String username) {
        if(username.equals("admin")) {
            return "admin";
        }else {
            return "user";
        }
    }
    public static User getUserByUsername(String username) {
        User user = new User();
        if(username.equals("admin")) {
            user.setUsername("admin");
            user.setPassword("123");
            user.setRole("admin");
        }else {
            user.setUsername("admin1");
            user.setPassword("456");
            user.setRole("user");
        }
        user.setState("1");
        return user;
    }
}
复制代码


定义一个首页控制器:

@RestController
public class IndexController {
    @RequestMapping("/index")
    public String index(Model model) {
        // 登录成后,即可通过Subject获取登录的用户信息
        User user = (User) SecurityUtils.getSubject().getPrincipal();
        model.addAttribute("user", user);
        return "index";
    }
}
复制代码


定义一个登录控制器:

@RestController
public class LoginController {
    @GetMapping("/login")
    public String login() {
        return "login";
    }
    @PostMapping("/login")
    @ResponseBody
    public String login(String username, String password) {
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        // 获取Subject对象
        Subject subject = SecurityUtils.getSubject();
        try {
            // 执行登录
            subject.login(token);
            return "ok";
        } catch (UnknownAccountException e) {
            return e.getMessage();
        } catch (IncorrectCredentialsException e) {
            return "IncorrectCredentialsException " + e.getMessage();
        } catch (LockedAccountException e) {
            return "LockedAccountException " + e.getMessage();
        } catch (AuthenticationException e) {
            return "认证失败!";
        }
    }
}
复制代码


运行项目,访问: http://localhost:8878/api/index,  发现没法直接访问自动跳转到了http://localhost:8878/api/login;jsessionid=F7E2EABECB7D87B89EA99C328B344B05,说明我们还未登录, 下面进行登录 POST http://localhost:8878/api/login, 登录成功后会自动跳转/index, 并且返回结果 index --->admin, 说明我们之前的配置都生效了,并且可以通过Subject拿到用户信息


结束语

本期内容就到这里结束了, 总结一下主要讲了在SpringBoot中如何集成Shiro,以及带大家学习了它的基本配置,最后我们完成了一个用户认证的功能。


下期预告

细心的同学可能会发现,当我们浏览器关闭在进来访问首页index的时候,发现又跳转回了登录,这样的交互肯定是不友好的,所以下期带大家学习如何在Shiro中存储我们的Cookie,让它记住我们的信息

相关文章
|
2月前
|
缓存 安全 Java
Shiro框架以及Spring Boot整合Shiro
Shiro框架以及Spring Boot整合Shiro
Shiro框架以及Spring Boot整合Shiro
|
2月前
|
开发框架 安全 Java
【Java专题_01】springboot+Shiro+Jwt整合方案
【Java专题_01】springboot+Shiro+Jwt整合方案
|
2月前
|
Java 关系型数据库 MySQL
Shiro实战+springboot集成
Shiro实战+springboot集成
17 0
|
2月前
|
前端开发 Java 关系型数据库
【Shiro】Springboot集成Shiro
【Shiro】Springboot集成Shiro
57 1
|
3月前
|
Java
springboot如何配置使用shiro
【1月更文挑战第11天】springboot如何配置使用shiro
19 2
|
3月前
|
存储 Java 数据库
SpringBoot+JWT+Shiro
SpringBoot+JWT+Shiro
30 0
|
3月前
|
存储 JSON 安全
使用shiro对数据库中的密码进行加密存储(java+springboot+shiro)
使用shiro对数据库中的密码进行加密存储(java+springboot+shiro)
91 0
|
3月前
|
前端开发 Java Spring
使用Spring Boot集成Shiro时出现了无法注入Service的问题
使用Spring Boot集成Shiro时出现了无法注入Service的问题
|
4月前
|
Java
Springboot整合之Shiro和JWT技术实现无感刷新9
Springboot整合之Shiro和JWT技术实现无感刷新9
|
4月前
|
JSON 安全 Java
Springboot整合之Shiro和JWT技术实现无感刷新8
Springboot整合之Shiro和JWT技术实现无感刷新8