乐趣区

关于springboot:使用shiro进行登录认证案例1

开发步骤

1. 导入相干依赖(一共 10 个)

2. 控制器编写 2 个办法(注册和登录)

3. 自定义认证受权处理器,继承 AuthorizingRealm 类

4. 实现 doGetAuthenticationInfo 认证办法

5. 解决认证办法

5.1. 获取用户名
5.2. 调用 service 成判断用户是否存在
5.3. 判断用户的状态
5.4. 返回一个 SimpleAuthenticationInfo 认证对象

6. 编写 shiro 配置类(ShiroConfig)

6.1 配置 ShiroFilterFactoryBean 注入 SecurityManager
6.2 配置 SecurityManager 注入咱们自定义的认证处理器(ShiroRealm)
6.3. 配置自定义认证处理器(ShiroRealm)注入自定义明码匹配器(CredentialsMatcher)

7. 自定义明码匹配器(继承 SimpleCredentialsMatcher 类),重写 doCredentialsMatch 办法

7.1 自定义加密解密工具类实现
7.2 应用 BCryptPasswordEncoder 类实现

8. 配置 application.yml 文件

8.1MySQL 数据源
8.2 通用 mapper 的代理类门路

9. 异样信息相应解决(可有可无)

10. 测试性能,注册和登录

1.pom 依赖

<?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 https://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.1.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>
    <!-- 测试启动器 -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--springMVC-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 整合 Mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!--mysql 数据源 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- 通用 mapper-->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>
        <!--spring 整合 shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>

        <!-- 日志打印 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <!--lombok 懒人依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--JSON 转换 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.71</version>
        </dependency>
        <!-- 加密 -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-crypto</artifactId>
            <version>5.3.3.RELEASE</version>
        </dependency>
    </dependencies>

</project>

2.controller 的 2 个办法:增加和登录

package com.huacheng.controller;

import com.huacheng.domain.User;
import com.huacheng.response.ResponseVO;
import com.huacheng.service.UserService;
import com.huacheng.util.PasswordUtil;
import com.huacheng.util.ResultUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Log4j2
@RestController
public class LoginController {

    @Autowired
    private UserService userService;

    /**
     * 登录
     *
     * @param username
     * @param password
     * @param rememberMe
     * @return
     */
    @RequestMapping("/login")
    public ResponseVO login(String username, String password, Boolean rememberMe) {
        UsernamePasswordToken token = null;
        try {
            // 登录
            token = new UsernamePasswordToken(username, password, false);
            // 获取以后的登录对象
            Subject currentUser = SecurityUtils.getSubject();
            currentUser.login(token);
            return ResultUtil.success("登录胜利:"+username+"用户欢迎您");
        } catch (Exception e) {log.info("登录失败用户名 {}", username, e);
            token.clear();
            return ResultUtil.error(e.getMessage());

        }
    }

    @RequestMapping("/add")
    public ResponseVO add(User user) {

        try {userService.add(user);
        } catch (Exception e) {log.info("新增失败:{}", user);
            return ResultUtil.error("增加失败");

        }
        log.info("新增用户:{}", user);
        return ResultUtil.success("增加胜利");
    }
}

3. 编写自定义认证受权处理器(ShiroRealm)

package com.huacheng.realms;

import com.huacheng.domain.User;
import com.huacheng.service.UserService;
import com.huacheng.util.PasswordUtil;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.print.DocFlavor;

@Log4j2
//@Component
public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    /**
     * 受权的办法
     *
     * @param principal
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {return null;}
    /**
     * 认证的办法
     *
     * @param token
     * @return
     */

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {

        //1. 获取用户名
        String username = (String) token.getPrincipal();
        User user = userService.findUserByUsername(username);
        //2. 判断用户的账号是否存在
        if (user == null) {log.info("用户名或者明码谬误 user:{}", user);
            throw new UnknownAccountException("用户名或者明码谬误");
        }

        //3. 判断用户的状态是否可用
        if (user.getStatus() == 0) {log.info("帐号已被锁定,禁止登录,user:{}", user);
            throw new LockedAccountException("帐号已被锁定,禁止登录!");
        }

        //4. 返回一个认证对象
        log.info("登录胜利:{}", user.getUsername() + "用户欢迎您");
        return new SimpleAuthenticationInfo(user.getId(),
                user.getPassword(),
                getName());
    }
}

4. 编写 shiro 配置类(ShiroConfig)

package com.huacheng.config;

import com.huacheng.credentials.CredentialsMatcher;
import com.huacheng.realms.ShiroRealm;
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;

@Configuration
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
      /*  // 如果不设置默认会主动寻找 Web 工程根目录下的 "/login.jsp" 页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录胜利后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index.html");*/
        return shiroFilterFactoryBean;
    }

    @Bean
    public SecurityManager securityManager(ShiroRealm myRealms) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 注入咱们自定义的认证受权处理器
        securityManager.setRealm(myRealms);
        return securityManager;
    }


    @Bean
    public ShiroRealm shiroRealm(CredentialsMatcher credentialsMatcher) {ShiroRealm myRealms = new ShiroRealm();
        // 注入自定义的明码匹配器
        myRealms.setCredentialsMatcher(credentialsMatcher);
        return myRealms;
    }


}

5. 自定义明码匹配器(这里我用 2 中形式都可行)

package com.huacheng.credentials;

import com.huacheng.util.PasswordUtil;
import lombok.extern.java.Log;
import lombok.extern.log4j.Log4j2;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

/**
 * Shiro- 明码凭证匹配器(验证明码有效性)*/
@Log4j2
@Component
public class CredentialsMatcher extends SimpleCredentialsMatcher {


    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {UsernamePasswordToken utoken = (UsernamePasswordToken) token;
        //1. 取得用户输出的明文明码
        String loginPassword = new String(utoken.getPassword());
        //2. 取得数据路的密文明码
        String dbPassword = (String) info.getCredentials();
        //3. 进行明码的比对


        // 第一种形式,应用本人的加密解密办法比对明码
      /*  String decryptPassword = null;
        try {decryptPassword = PasswordUtil.decrypt(dbPassword, utoken.getUsername());
        } catch (Exception e) {log.info("明码解密失败 {}", loginPassword);
            throw new RuntimeException("明码解密失败");
        }
        if (!decryptPassword.equals(loginPassword)) {throw new IncorrectCredentialsException("用户名或者明码谬误");
        }*/

        // 第二种形式,应用 BCryptPasswordEncoder 进行解密
        if (!passwordEncoder.matches(loginPassword, dbPassword)) {throw new IncorrectCredentialsException("用户名或者明码谬误");
        }
        return true;
    }
}

6. 对立异样解决的控制器

package com.huacheng.controller;

import com.huacheng.enums.ResponseStatus;
import com.huacheng.exception.Myexception;
import com.huacheng.response.ResponseVO;
import com.huacheng.util.CommonConst;
import com.huacheng.util.ResultUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.lang.reflect.UndeclaredThrowableException;

/**
 * 对立异样解决类 <br>
 * 捕捉程序所有异样,针对不同异样,采取不同的解决形式
 *
 * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
 * @version 1.0
 * @website https://www.zhyd.me
 * @date 2018/4/24 14:37
 * @since 1.0
 */
@Slf4j
@ControllerAdvice
public class ExceptionHandleController {@ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseVO handle(Throwable e) {if (e instanceof Myexception) {return ResultUtil.error(e.getMessage());
        }
        if (e instanceof UndeclaredThrowableException) {e = ((UndeclaredThrowableException) e).getUndeclaredThrowable();}
        ResponseStatus responseStatus = ResponseStatus.getResponseStatus(e.getMessage());
        if (responseStatus != null) {log.error(responseStatus.getMessage());
            return ResultUtil.error(responseStatus.getCode(), responseStatus.getMessage());
        }
        e.printStackTrace(); // 打印异样栈
        return ResultUtil.error(CommonConst.DEFAULT_ERROR_CODE, ResponseStatus.ERROR.getMessage());
    }

}
退出移动版