天天看点

SpringBoot 项目(若依脚手架)3

前言:

紧接着 上一章 “SpringBoot 项目(若依脚手架)2” 补充

5.1.2.1 MessageUtils 工具类中,使用到的 SpringUtils 工具类

package com.ruoyi.common.utils.spring;

import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

/**
 * spring工具类 方便在非spring管理环境中获取bean
 *
 * @author ruoyi
 */
@Component
public final class SpringUtils implements BeanFactoryPostProcessor
{
    /** Spring应用上下文环境 */
    private static ConfigurableListableBeanFactory beanFactory;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
    {
        SpringUtils.beanFactory = beanFactory;
    }

    /**
     * 获取对象
     *
     * @param name
     * @return Object 一个以所给名字注册的bean的实例
     * @throws BeansException
     *
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException
    {
        return (T) beanFactory.getBean(name);
    }

    /**
     * 获取类型为requiredType的对象
     *
     * @param clz
     * @return
     * @throws BeansException
     *
     */
    public static <T> T getBean(Class<T> clz) throws BeansException
    {
        T result = (T) beanFactory.getBean(clz);
        return result;
    }

    /**
     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
     *
     * @param name
     * @return boolean
     */
    public static boolean containsBean(String name)
    {
        return beanFactory.containsBean(name);
    }

    /**
     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
     *
     * @param name
     * @return boolean
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.isSingleton(name);
    }

    /**
     * @param name
     * @return Class 注册对象的类型
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getType(name);
    }

    /**
     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
     *
     * @param name
     * @return
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getAliases(name);
    }

    /**
     * 获取aop代理对象
     *
     * @param invoker
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAopProxy(T invoker)
    {
        return (T) AopContext.currentProxy();
    }
}
           

5.2 用户相关的异常类  

CaptchaException :验证码错误异常类

package com.ruoyi.common.exception.user;

/**
 * 验证码错误异常类
 * @author liangcy
 * @create 2019/10/12 - 10:33
 */
public class CaptchaException extends UserException{
    private static final long serialVersionUID = 1L;

    public CaptchaException() {
        super("user.jcaptcha.error", null);
    }
}
           
RoleBlockedException: 角色锁定异常类
           
package com.ruoyi.common.exception.user;

/**
 * 角色锁定异常类
 * 
 * @author ruoyi
 */
public class RoleBlockedException extends UserException
{
    private static final long serialVersionUID = 1L;

    public RoleBlockedException()
    {
        super("role.blocked", null);
    }
}
           
UserBlockedException : 用户锁定异常
           
package com.ruoyi.common.exception.user;

/**
 * @author liangcy
 * @create 2019/10/13 - 11:10
 */
public class UserBlockedException extends UserException {
    private static final long serialVersionUID = 1L;

    public UserBlockedException() {
        super("user.blocked", null);
    }
}
           
UserDeleteException : 用户被删除异常
           
package com.ruoyi.common.exception.user;

/**
 * @author liangcy
 * @create 2019/10/13 - 11:09
 */
public class UserDeleteException extends UserException {
    private static final long serialVersionUID = 1L;

    public UserDeleteException() {
        super("user.password.delete", null);
    }
}
           
UserException : 用户异常
           
package com.ruoyi.common.exception.user;

import com.ruoyi.common.exception.base.BaseException;

/**
 * @author liangcy
 * @create 2019/10/12 - 10:31
 */
public class UserException extends BaseException {
    private static final long serialVersionUID = 1L;

    public UserException(String code, Object[] args) {
        super("user", code, args, null);
    }

}
           
UserNotExistsException :用户不存在异常
           
package com.ruoyi.common.exception.user;

/**
 * @author liangcy
 * @create 2019/10/12 - 10:39
 */
public class UserNotExistsException extends UserException {

    private static final long serialVersionUID = 1L;

    public UserNotExistsException() {
        super("user.not.exists", null);
    }

}
           
UserPasswordNotMatchException : 密码错误异常
           
package com.ruoyi.common.exception.user;

/**
 * @author liangcy
 * @create 2019/10/12 - 10:47
 */
public class UserPasswordNotMatchException extends UserException {

    private static final long serialVersionUID = 1L;

    public UserPasswordNotMatchException(){
        super("user.password.not.match", null);
    }
}
           

六、Service层

6.1  SysPasswordService 类:主要是用来验证登录密码的,后期会加上 登录次数限制、异步记录、缓存的功能

package com.ruoyi.framework.shiro.service;

import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.system.domain.SysUser;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.springframework.stereotype.Component;

/**
 * 登录密码方法
 *
 * @author liangcy
 * @create 2019/10/13 - 11:12
 */
@Component
public class SysPasswordService {
    public void validate(SysUser user, String password) {

        String loginName = user.getLoginName();
        //TODO 从缓存中获取登录次数

        if (!matches(user, password)) {
            //TODO 1、异步记录日志 2、将登录次数存入缓存中
            throw new UserPasswordNotMatchException();
        } else {
            //TODO 密码正确,清除缓存中用户名
        }
    }

    private boolean matches(SysUser user, String password) {
        return user.getPassword().equals(encryptPassword(user.getLoginName(), password, user.getSalt()));
    }

    public String encryptPassword(String username, String password, String salt) {
        return new Md5Hash(username + password + salt).toHex().toString();
    }
}
           

6.2 ISysUserService  用户 业务层接口

package com.ruoyi.system.service;

import com.ruoyi.system.domain.SysUser;

import java.util.List;

/**
 * @author liangcy
 * @create 2019/10/12 - 10:52
 */
public interface ISysUserService {

    /**
     * 通过用户名查询用户
     *
     * @param userName 用户名
     * @return 用户对象信息
     */
    public SysUser selectUserByLoginName(String userName);

    /**
     * 通过手机号码查询用户
     *
     * @param phoneNumber 手机号码
     * @return 用户对象信息
     */
    public SysUser selectUserByPhoneNumber(String phoneNumber);
    /**
     * 通过邮箱查询用户
     *
     * @param email 邮箱
     * @return 用户对象信息
     */
    public SysUser selectUserByEmail(String email);
   
}
           

6.2.1 SysUserServiceImpl :ISysUserService 接口的实现类

package com.ruoyi.system.service.impl;

import com.ruoyi.system.domain.SysUser;
import com.ruoyi.system.mapper.SysUserMapper;
import com.ruoyi.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;


/**
 * @author liangcy
 * @create 2019/10/13 - 9:59
 */
@Service
@Slf4j
public class SysUserServiceImpl implements ISysUserService {

    @Resource
    private SysUserMapper userMapper;

    @Override
    public SysUser selectUserByLoginName(String userName) {
        return userMapper.selectUserByLoginName(userName);
    }

    @Override
    public SysUser selectUserByPhoneNumber(String phoneNumber) {
        return userMapper.selectUserByPhoneNumber(phoneNumber);
    }

    @Override
    public SysUser selectUserByEmail(String email) {
        return userMapper.selectUserByEmail(email);
    }

}
           

6.3 SysLoginSerivice : 处理登录的业务逻辑类

package com.ruoyi.framework.shiro.service;

import com.ruoyi.common.constant.ShiroConstants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.user.*;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.system.domain.SysUser;
import com.ruoyi.system.service.ISysUserService;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;

/**
 * @author liangcy
 * @create 2019/10/11 - 23:10
 */
@Component
public class SysLoginSerivice {
    @Resource
    private ISysUserService userService;

    @Resource
    private SysPasswordService passwordService;

    /**
     * 登录
     */
    public SysUser login(String username, String password) {
        //验证码校验
        if (!StringUtils.isEmpty(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA))) {
            //TODO 异步方法记录日志 验证码错误
            throw new CaptchaException();
        }
        // 用户名或密码为空 错误
        if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
            //TODO 异步方法记录日志 用户名或密码为空
            throw new UserNotExistsException();
        }
        // 密码如果不在指定范围内 错误
        if(password.length() < UserConstants.PASSWORD_MIN_LENGTH || password.length() > UserConstants.PASSWORD_MAX_LENGTH ){
            //TODO 记录日志
            throw new UserPasswordNotMatchException();
        }

        //查询用户信息
        SysUser user = userService.selectUserByLoginName(username);

        if(user == null && maybeMobilePhoneNumber(username)){
            user = userService.selectUserByPhoneNumber(username);
        }
        if( user == null && maybeEmail(username)) {
            user = userService.selectUserByEmail(username);
        }
        if( user == null){
            //TODO 异步记录日志
            throw new UserNotExistsException();
        }
        if(UserStatus.DELETED.getCode().equals(user.getDelFlag())){
            //TODO 异步记录日志
            throw new UserDeleteException();
        }
        if(UserStatus.DISABLE.getCode().equals(user.getStatus())){
            //TODO 异步记录日志
            throw new UserBlockedException();
        }

        //校验密码是否正确
        passwordService.validate(user, password);
        //TODO 异步记录日志,登录成功的信息
        //TODO 记录登录信息
        return user;
    }

    private boolean maybeEmail(String username) {
        if(!username.matches(UserConstants.EMAIL_PATTERN)){
            return false;
        }
        return true;
    }

    private boolean maybeMobilePhoneNumber(String username) {
        if(!username.matches(UserConstants.MOBILE_PHONE_NUMBER_PATTERN)){
            return false;
        }
        return true;
    }
}
           

6.3.1 ServletUtils :客户端工具类

package com.ruoyi.common.utils;

import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * @author liangcy
 * @create 2019/10/11 - 23:22
 */
public class ServletUtils {

    /**
     * 获取request
     */
    public static HttpServletRequest getRequest(){
        return getRequestAttributes().getRequest();
    }

    public static ServletRequestAttributes getRequestAttributes(){
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return (ServletRequestAttributes) attributes;
    }
}
           

6.3.2 UserConstants : 用户常量信息

package com.ruoyi.common.constant;

/**
 * @author liangcy
 * @create 2019/10/12 - 10:44
 */
public class UserConstants {

    /** 部门正常状态 */
    public static final String DEPT_NORMAL = "0";
    /**
     * 密码长度限制
     */
    public static final int PASSWORD_MIN_LENGTH = 5;
    public static final int PASSWORD_MAX_LENGTH = 20;

    /** 登录名称是否唯一的返回结果码 */
    public final static String USER_NAME_UNIQUE = "0";
    public final static String USER_NAME_NOT_UNIQUE = "1";
    /** e-mail 是否唯一的返回结果 */
    public final static String USER_EMAIL_UNIQUE = "0";
    public final static String USER_EMAIL_NOT_UNIQUE = "1";
    /** 手机号码是否唯一的返回结果 */
    public final static String USER_PHONE_UNIQUE = "0";
    public final static String USER_PHONE_NOT_UNIQUE = "1";
    /** 角色名称是否唯一的返回结果码 */
    public final static String ROLE_NAME_UNIQUE = "0";
    public final static String ROLE_NAME_NOT_UNIQUE = "1";
    /** 角色权限是否唯一的返回结果码 */
    public final static String ROLE_KEY_UNIQUE = "0";
    public final static String ROLE_KEY_NOT_UNIQUE = "1";
    /** 部门名称是否唯一的返回结果码 */
    public final static String DEPT_NAME_UNIQUE = "0";
    public final static String DEPT_NAME_NOT_UNIQUE = "1";
    /** 岗位名称是否唯一的返回结果码 */
    public final static String POST_NAME_UNIQUE = "0";
    public final static String POST_NAME_NOT_UNIQUE = "1";
    /** 岗位编码是否唯一的返回结果码 */
    public final static String POST_CODE_UNIQUE = "0";
    public final static String POST_CODE_NOT_UNIQUE = "1";

    /** 菜单名称是否唯一的返回结果码 */
    public final static String MENU_NAME_UNIQUE = "0";
    public final static String MENU_NAME_NOT_UNIQUE = "1";
    /** 字典类型是否唯一的返回结果码 */
    public final static String DICT_TYPE_UNIQUE = "0";
    public final static String DICT_TYPE_NOT_UNIQUE = "1";

    /**
     * 手机号码格式限制
     */
    public static final String MOBILE_PHONE_NUMBER_PATTERN = "^0{0,1}(13[0-9]|15[0-9]|14[0-9]|18[0-9])[0-9]{8}$";
    /**
     * 邮箱格式限制
     */
    public static final String EMAIL_PATTERN = "^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?";
}
           

6.3.3 UserStatus :用户状态

package com.ruoyi.common.enums;

import lombok.Getter;

/**
 * 用户状态
 *
 * @author liangcy
 * @create 2019/10/13 - 11:04
 */
@Getter
public enum UserStatus {

    OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除");

    private final String code;
    private final String info;

    UserStatus(String code, String info) {
        this.code = code;
        this.info = info;
    }

}
           

七、测试 SysLoginSerivice 的登录方法

package com.ruoyi.framework.shiro.service;

import com.ruoyi.system.domain.SysUser;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

import static org.junit.Assert.*;

/**
 * @author liangcy
 * @create 2019/11/1 - 13:28
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class SysLoginSeriviceTest {

    @Resource
    SysLoginSerivice loginSerivice;
    @Test
    public void login() {
        SysUser user = loginSerivice.login("admin", "1234");
        System.out.println(user);
    }
}
           

测试结果:

用户名密码正确:

SpringBoot 项目(若依脚手架)3

用户名或密码错误:

SpringBoot 项目(若依脚手架)3

继续阅读