Shiro系列(四)--- Springboot整合Shiro实现基本的登录认证流程

继续我们shiro系列博客相关的学习笔记,前几篇只是大体对shiro的基本概念、可以实现的基本功能、并通过官网提供的简单示例大体了解了一下shiro,感觉挺干的,不想再这样整理下去了,像是什么INI配置、Realm、拦截器机制、会话管理这些不再单独整理博客记录了,之后都通过代码的方式来体现并不断拓展;各位看到此博客的小伙伴,如有不对的地方请及时通过私信我或者评论此博客的方式指出,以免误人子弟。多谢!

这一篇我们使用Springboot整合Shiro,从用户登录到验证、授权整个流程通过代码简单梳理一下。

目录

基础准备环境

首先我们引入相关依赖

数据库建表

 login.html index.html

登录验证 

正式的业务代码

LoginController:

配置下application.yml:

自定义Realm

配置拦截器

测试


基础准备环境

首先我们引入相关依赖

        <!-- SpringBoot Web容器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Mysql驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- SpringBoot集成 mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>

        <!-- SpringBoot集成thymeleaf模板 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!--Shiro核心框架 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.7.1</version>
        </dependency>

        <!-- Shiro使用Srping框架 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.7.1</version>
        </dependency>

        <!-- thymeleaf模板引擎和shiro框架的整合 -->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

数据库建表

CREATE TABLE t_user (
  id int(11) NOT NULL AUTO_INCREMENT,
  user_name varchar(36) DEFAULT NULL,
  user_passwd varchar(36) DEFAULT NULL,
  age int(11) DEFAULT NULL,
  sex char(4) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

 添加一条测试数据:

insert into t_user values(1,'yjh','admin',12,'1');

 login.html index.html

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Login Page</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body >

<div>
    <form method="post" th:action="@{/login}">
        UserName:<input name="username" th:type="text">
        PassWord:<input name="password" th:type="password">
        Submit:<input type="submit"  value="Submit" />
    </form>
</div>

</body>
</html>
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Login Page</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body >
登录成功!
</body>
</html>

登录验证 

登录验证逻辑我们写在新加的LoginManager中,方便以后进行扩展时代码臃肿,这里只是简单进行了用户名和密码的验证,后续会不断完善,比如验证码验证,密码重试次数限制等。

@Component
public class LoginManager {

    @Autowired
    private UserManager userManager;

    /**
     * 登录验证
     */
    public void login(String username, String password){
        User user = userManager.findByUserName(username);
        if(user == null){
            throw new BusinessException("用户不存在",409);
        }
        if(!"123".equals(password)){
            throw new BusinessException("用户名或密码错误",409);
        }
    }
}

实体类、yml配置、Mapper等就不贴代码了,下面用到的时候再补上。

正式的业务代码

LoginController:

@Controller
public class LoginController {

    /**
     * 跳转到登录页面
     */
    @GetMapping("/login")
    public String login() {
        return "login";
    }

    /**
     * 登录验证
     */
    @PostMapping("/login")
    public String login(@RequestParam("username") String username,
                        @RequestParam("password") String password) {
        // 1、收集主题提交的身份和凭证
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            // 2、提交主题的身份和凭证进行身份验证。
            Subject subject = SecurityUtils.getSubject();
            subject.login(token);
        } catch (Exception e) {
            System.out.println(e.getCause());
        }
        return "index";
    }
}

配置下application.yml:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3307/shiro?serverTimezone=UTC
    username: root
    password: root
  thymeleaf:
    mode: html
    encoding: utf-8
    # 是否缓存,开发模式下设置为false,避免改了模板还要重启服务器;线上设置为true,可以提高性能。
    cache: false
    # 指定模板所在的目录
    prefix: classpath:/
mybatis-plus:
  configuration:
    # # sql输出到控制台  方便查看
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

注意上面配置thymeleaf模板所在的路径要准确,prefix默认会去classpath:/templates/ 目录下去找模板,从提示上即可看出:

 如果配置错误,当你访问login页面时浏览器会报如下错误:

 控制台会报如下错误: 

org.thymeleaf.exceptions.TemplateInputException: Error resolving template [login], template might not exist or might not be accessible by any of the configured Template Resolvers

自定义Realm

 由前面几篇对shiro的学习,我们知道认证是委托给SecurityManager来做的,而SecurityManager需要Realm来加载用户数据,下面我们定义一下我们自己的Realm进行认证和授权,下面只是进行登录认证:

public class CustomRealm extends AuthorizingRealm {
    @Autowired
    private UserManager userManager;
    @Autowired
    private LoginManager loginManager;

    /**
     * 认证: 登录时调用
     * AuthenticationToken 用于收集用户提交的身份(如用户名)及凭据(如密码)
     * Object getPrincipal() --- 身份
     * Object getCredentials() --- 凭据
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken utoken = (UsernamePasswordToken) token;
        // 登录验证逻辑
        String username = utoken.getUsername();
        String password = new String(utoken.getPassword());
        loginManager.login(username,password);
        // 如果身份认证验证成功,返回一个AuthenticationInfo实现,保存主体和凭据。
        return new SimpleAuthenticationInfo(utoken.getUsername(),new String(utoken.getPassword()),getName());
    }

    /**
     * 授权
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
}

配置拦截器

回想在使用SpringMvc的时候,通常都需要在web.xml中配置一个拦截器,然后在Spring的xml中配置拦截器的bean对象,比如像下面这样:

<filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.apache.shiro.web.servlet.IniShiroFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
   <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
      <property name="securityManager" ref="securityManager"/>
   </bean>

 现在我们使用Springboot整合Shiro时也是一样的道理,需要在我们的配置文件中配置拦截器,拦截器可以看做是程序的入口,认证、授权等都需要SecurityManager,那少不了需要将SecurityManager注入到拦截器中,如下:

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

如果不配置ShiroFilterFactoryBean,登录的时候会报错,错误信息如下:

org.apache.shiro.UnavailableSecurityManagerException: No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton.  This is an invalid application configuration.

 像是UserManager还有Mapper这些,不贴代码了,就是一个简单的查询数据库的方法。

测试

到此,一个基本的Springboot 整合 Shiro的项目就完成了,包含登录和登录验证,并没有授权和权限的验证,下一篇继续完善。

访问 http://localhost:8080/login 进入到登录页面:

输入用户名密码登录后跳到成功页面:

输入错误的用户名或密码,因验证不通过停留在login页面。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值