springboot整合shiro多验证登录功能的实现(账号密码登录和使用手机验证码登录)

SpringBoot整合Shiro多验证登录功能的实现

SpringBoot是一个快速开发Spring应用的框架,而Shiro可以方便的实现安全认证和授权,两者结合,可以非常方便的实现多验证登录功能。

SpringBoot集成Shiro

首先需要添加Shiro和SpringBoot的依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.7.1</version>
</dependency>

然后需要配置Shiro的SpringBoot启动类,添加@Configuration注解,并添加@Bean注解的ShiroFilterFactoryBean和DefaultWebSecurityManager。

@Configuration
public class ShiroConfiguration {
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        ...
        return shiroFilterFactoryBean;
    }

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

    @Bean
    public MyRealm myRealm() {
        return new MyRealm();
    }
}

自定义Realm

接着需要自定义Realm,实现doGetAuthenticationInfo和doGetAuthorizationInfo方法。

public class MyRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //授权方法
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //认证方法
        return null;
    }
}

账号密码登录验证

对于账号密码登录验证,可以通过用户名和密码进行登录认证,实现doGetAuthenticationInfo方法。

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
    String username = token.getUsername();
    String password = new String(token.getPassword());

    //根据用户名和密码查询用户信息,可以通过MyBatis进行数据库操作
    User user = userService.findByUsernameAndPassword(username, password);
    if (user == null) {
        throw new UnknownAccountException("用户名或密码错误");
    }

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

关于User实体和UserService可以通过自己的业务逻辑进行实现。

手机验证码登录验证

对于手机验证码登录验证,则需要在认证方法中自定义Token,用来存储手机号和验证码的信息。

public class SmsToken implements AuthenticationToken {
    private String phone;
    private String code;

    public SmsToken(String phone, String code) {
        this.phone = phone;
        this.code = code;
    }

    @Override
    public Object getPrincipal() {
        return phone;
    }

    @Override
    public Object getCredentials() {
        return code;
    }
}

同时需要实现SmsRealm,在认证方法中进行手机号和验证码的比对。

public class SmsRealm extends AuthenticatingRealm {
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        SmsToken token = (SmsToken) authenticationToken;
        String phone = token.getPrincipal().toString();
        String code = token.getCredentials().toString();

        //查询手机号和验证码是否匹配
        if (!"123456".equals(code)) {
            throw new AuthenticationException("验证码错误");
        }

        return new SimpleAuthenticationInfo(phone, code, getName());
    }
}

完整代码可以参考以下示例:

@Configuration
public class ShiroConfiguration {
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/doLogin", "anon");
        filterChainDefinitionMap.put("/login/sms", "anon");
        filterChainDefinitionMap.put("/doSmsLogin", "anon");
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

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

    @Bean
    public MyRealm myRealm() {
        return new MyRealm();
    }

    @Bean
    public SmsRealm smsRealm() {
        return new SmsRealm();
    }
}

public class MyRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //授权方法
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();
        String password = new String(token.getPassword());

        //根据用户名和密码查询用户信息,可以通过MyBatis进行数据库操作
        User user = userService.findByUsernameAndPassword(username, password);
        if (user == null) {
            throw new UnknownAccountException("用户名或密码错误");
        }

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

public class SmsRealm extends AuthenticatingRealm {
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        SmsToken token = (SmsToken) authenticationToken;
        String phone = token.getPrincipal().toString();
        String code = token.getCredentials().toString();

        //查询手机号和验证码是否匹配
        if (!"123456".equals(code)) {
            throw new AuthenticationException("验证码错误");
        }

        return new SimpleAuthenticationInfo(phone, code, getName());
    }
}

@Controller
public class LoginController {
    @GetMapping("/login")
    public String login() {
        return "login";
    }

    @GetMapping("/logout")
    public String logout() {
        return "logout";
    }

    @PostMapping("/doLogin")
    public String doLogin(String username, String password) {
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
            return "redirect:/index";
        } catch (AuthenticationException e) {
            return "error";
        }
    }

    @GetMapping("/login/sms")
    public String smsLogin() {
        return "login_sms";
    }

    @PostMapping("/doSmsLogin")
    public String doSmsLogin(String phone, String code) {
        SmsToken token = new SmsToken(phone, code);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
            return "redirect:/index";
        } catch (AuthenticationException e) {
            return "error";
        }
    }
}

其中包括了一个基本的登录页面、验证码登录页面,以及对应的Controller。在前端页面的form中添加对应的input即可实现不同方式的登录。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:springboot整合shiro多验证登录功能的实现(账号密码登录和使用手机验证码登录) - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • 基于Class.forName()用法及说明

    下面我来为你详细讲解“基于Class.forName()用法及说明”的完整攻略。 什么是Class.forName()方法? 在Java中,Class.forName()方法是一种加载Class对象的方式。注意,它并不是使用了一个类,而是将它加载到JVM中,使其代码可以被执行。通过使用该方法,我们可以动态的创建对象、使用反射等功能。 Class.forNam…

    Java 2023年6月15日
    00
  • SpringBoot多种自定义错误页面方式小结

    首先我们来介绍一下SpringBoot的错误页面。SpringBoot的错误页面一般可以分为以下两种: 默认错误页面 SpringBoot自带了默认的错误页面,在出现错误时会自动跳转到该页面。默认的错误页面包含了错误的状态码、错误信息和错误堆栈等信息。如果你没有设置自定义的错误页面,那么就会默认跳转到该页面。 自定义错误页面 SpringBoot还支持开发者…

    Java 2023年5月25日
    00
  • ​​​​​​​Spring多租户数据源管理 AbstractRoutingDataSource

    下面是关于Spring多租户数据源管理的完整攻略。 什么是Spring多租户数据源管理? Spring多租户数据源管理是指在一个应用程序中,为不同的租户(tenant)提供不同的数据库连接,并通过一个中心路由器(AbstractRoutingDataSource)将相应的数据库连接与请求的租户关联起来,实现多租户级别的数据隔离。 AbstractRoutin…

    Java 2023年6月2日
    00
  • Spring Boot启动banner定制的步骤详解

    下面我将详细讲解 Spring Boot 启动 banner 定制的步骤详解。 什么是 Spring Boot Banner? 首先需要了解什么是 Spring Boot Banner。在 Spring Boot 启动的时候,会默认显示一个文本横幅(Banner),这个 Banner 通常包含了项目的名称、版本号以及项目的官方网站等信息。如果我们想要自定义 …

    Java 2023年5月19日
    00
  • java整数(秒数)转换为时分秒格式的示例

    让我来详细讲解一下如何将 Java 中的整数(秒数)转换为时分秒格式。 思路分析 将秒数转换为时分秒格式,其实就是将秒数拆分为小时、分钟、秒三个部分,然后格式化输出。可以使用 Java 中的数学运算和字符串格式化实现。 具体操作如下: 计算出总秒数中包含的小时数、分钟数和秒数; 使用字符串格式化输出结果。 代码实现 下面是整数(秒数)转换为时分秒格式的示例代…

    Java 2023年5月20日
    00
  • spring配置文件加密方法示例

    《spring配置文件加密方法示例》的完整攻略如下: 一、背景 在某些情况下,我们需要在spring配置文件中保存一些敏感信息,比如数据库连接用户名和密码等,为了保证这些信息的安全性,我们需要对这些信息进行加密处理。 二、实现方法 1. 使用spring jasypt spring jasypt是一个基于Jasypt的Spring安全加密工具库,可以对Spr…

    Java 2023年6月15日
    00
  • java生成jar包的方法

    生成 Java 的 JAR 包一般有两种方法,下面我会为你详细讲解。 方法一:通过命令行生成 JAR 包 首先,我们需要将我们的 Java 代码编译成字节码文件,使用下列代码将 “Example.java” 编译为 “Example.class”: javac Example.java 接下来,我们需要创建一个 MANIFEST.MF 文件。在此文件中需要包…

    Java 2023年5月19日
    00
  • Kotlin编程基础语法编码规范

    Kotlin编程基础语法编码规范 1. 常见命名规范 在Kotlin语言中,标识符的命名规范如下: 包名使用小写字母: 包名应该全部使用小写字母,且不应该使用下划线或者其它特殊字符。 类名使用驼峰命名: 类名的首字母应该大写,驼峰命名,不使用下划线。 方法名使用小驼峰命名: 方法名的首字母应该小写,而后面的单词首字母应该大写。 常量名使用全大写字母: 常量名…

    Java 2023年6月1日
    00
合作推广
合作推广
分享本页
返回顶部