SpringBoot整合Shiro 之 用户名密码登录

SpringBoot整合Shiro 之 用户名密码登录

首先 使用Shiro 我们要大致了解清楚 什么是Shiro?它能帮我们实现什么功能?
根据官方文档介绍:Shiro 是一个功能强大且易于使用的Java安全框架,可以实现 身份验证、授权、加密和会话管理功能。
那么今天 我这里就主要 实现以下 身份验证功能,也就是用户登录啦。

1.数据库 这里提供一个简单 user表SQL

CREATE TABLE `ums_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `login_name` varchar(15) NOT NULL COMMENT '用户名',
  `pass_word` varchar(255) NOT NULL COMMENT '密码',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户表';

2.导入 相关依赖

        <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.BUILD-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.zh.shiro</groupId>
    <artifactId>springboot_shiro</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>



</project>

3.代码编写

3.1 连接数据库配置


spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/jintian?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true&verifyServerCertificate=false&useSSL=false    # 数据库名称
    username: root
    password: 123456
    dbcp2:
      min-idle: 5                                           # 数据库连接池的最小维持连接数
      initial-size: 5                                       # 初始化连接数
      max-total: 5                                          # 最大连接数
      max-wait-millis: 200                                  # 等待连接获取的最大超时时间


3.2 创建用户实体类



import java.time.LocalDateTime;

/**
 * @Author: zh
 * @Date: 2020/7/7 10:59
 */
public class User {
    private Long id;
    private String loginName;
    private String passWord;
    private LocalDateTime createTime;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }

    public String getPassWord() {
        return passWord;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }

    public LocalDateTime getCreateTime() {
        return createTime;
    }

    public void setCreateTime(LocalDateTime createTime) {
        this.createTime = createTime;
    }
}


3.3  创建 编写 UserMapper.java 和 UserMapper.xml

UserMapper.java

import com.zh.shiro.pojo.User;
import org.apache.ibatis.annotations.Param;

/**
 * @Author: zh
 * @Date: 2020/7/7 11:08
 */
public interface UserMapper {
    /**
     * 根据用户名查询 该用户
     * @return
     */
    User getUserByName(@Param("name") String name);

}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zh.shiro.mapper.UserMapper">
    <select id="getUserByName" resultType="com.zh.shiro.pojo.User">
        SELECT
            id,
            login_name AS 'loginName',
            pass_word AS 'passWord'
        FROM
            ums_user
        WHERE
            login_name = #{name}
    </select>
</mapper>

3.4 自定义Realm 是用来查询用户信息 保存到权限管理器(可保存 用户对应 的角色 和 权限信息)
CustomAuthorizingRealm.java

import com.zh.shiro.mapper.UserMapper;
import com.zh.shiro.pojo.User;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

/**
 * @Author: zh
 * @Date: 2020/7/7 11:20
 */
public class CustomAuthorizingRealm extends AuthorizingRealm {
    @Autowired
    private UserMapper userMapper;
    /**
     * AuthorizationInfo  权限认证
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    /**
     *AuthenticationInfo 身份验证
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 获取 前端传过来的用户名和密码
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        String username = usernamePasswordToken.getUsername();
        String password = new String(usernamePasswordToken.getPassword());
        // 效验用户名和密码是否为空
        if (StringUtils.isEmpty(username)){
            throw new NullPointerException("用户名不能为空");
        }
        if (StringUtils.isEmpty(password)){
            throw new NullPointerException("密码不能为空");
        }
        // 根据 当前用户名 获取数据库 用户信息
        User user = userMapper.getUserByName(username);
        if (null == user){
            throw new NullPointerException("该用户不存在");
        }
        //  principal:认证的实体信息,可以是username,也可以是数据库表对应的用户的实体对象
        Object principal = user;
        // shiro 效验凭证
        Object credentials = user.getPassWord();
        // 加盐 用户密码加密时用到
        ByteSource credentialsSalt = ByteSource.Util.bytes("zh");
        return new SimpleAuthenticationInfo(principal,credentials,credentialsSalt,this.getClass().getName());
    }
}

3.5 配置 ShiroConfig
ShiroConfig.java

import com.zh.shiro.realm.CustomAuthorizingRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Author: zh
 * @Date: 2020/7/7 11:34
 */
@Configuration
public class ShiroConfig {

    /**
     * shiroFilter 过滤
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 需要登陆的接口 访问未登录 或 token失效时 回调setLoginUrl里的接口
        shiroFilterFactoryBean.setLoginUrl("/api/401");
        // 登录成功后回调路径  前后端分离项目不必要
        // shiroFilterFactoryBean.setSuccessUrl("/");
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
        /**
         * anon  匿名也可以访问  ps:没登录就能访问
         * authc  需要登录以后才可以访问
         * 过滤链 是由上而下执行的  所以 一般把 /** 放在最下面
         */
        filterChainDefinitionMap.put("/api/test","authc");
        filterChainDefinitionMap.put("/**","anon");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    /**
     * 配置 安全管理器
     * @return
     */
    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(customAuthorizingRealm());
        return securityManager;
    }

    /**
     * 将自定义realm 加入容器
     * @return
     */
    @Bean
    public CustomAuthorizingRealm customAuthorizingRealm(){
        CustomAuthorizingRealm realm = new CustomAuthorizingRealm();
        realm.setCredentialsMatcher(hashedCredentialsMatcher());
        return realm;
    }

    /**
     *定义密码加密规则
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 设置散列算法
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 设置双层MD5  散列次数
        hashedCredentialsMatcher.setHashIterations(2);
        return hashedCredentialsMatcher;
    }
}


3.6 编写 UserController  因为没有 封装 返回结果集 所以 就直接返回的Map 凑合凑合吧 哈哈

UserController.java

import com.zh.shiro.pojo.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author: zh
 * @Date: 2020/7/7 11:49
 */
@RestController
@RequestMapping("/api")
public class UserController {

    /**
     * 用户登录接口
     * @param user
     * @return
     */
    @PostMapping("/login")
    public Object LigIn(@RequestBody User user){
        Map<String,Object> map = new HashMap<>();
        Subject subject = SecurityUtils.getSubject();
        try {
            UsernamePasswordToken token = new UsernamePasswordToken(user.getLoginName(),user.getPassWord());
            subject.login(token);
            map.put("code","200");
            map.put("message","success");
            map.put("token",subject.getSession().getId());
            return map;
        }catch (IncorrectCredentialsException e){
            map.put("code","501");
            map.put("message","密码不正确");
            return map;
        }
    }

    /**
     * 用户未登录 token失效时 访问其他接口时 shiroFilter 调用接口
     * @return
     */
    @GetMapping("/401")
    public Object _401(){
        Map<String,Object> map = new HashMap<>();
        map.put("code","401");
        map.put("message","not login");
        return map;
    }

    /**
     * 测试接口
     * @return
     */
    @GetMapping("/test")
    public Object test(){
        Map<String,Object> map = new HashMap<>();
        map.put("code","200");
        map.put("message","success");
        return map;
    }
}



3.7 测试环节 使用 postman测试
  • 先给数据库插入一条数据。因为 这里密码使用 MD5 加密了。所以我们先在代码里整一个测试密码出来。

在这里插入图片描述

  • 数据库
    在这里插入图片描述
  • 准备好了 就开始可以测试了
    在这里插入图片描述
    在这里插入图片描述
    再来 使用登录状态调用一下 test 接口
    在这里插入图片描述
    未登录状态调用 清掉 postman 里保存的token
    在这里插入图片描述

这里就可以看到 他回调的是 401 接口
在这里插入图片描述
个人感觉 这个springBoot 整合 shiro还是比较全的。 但只是单独的用户的登录认证,没有涉及到 权限之类的。所以权限 就留到下次吧。
代码已上传至 我的码云

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值