天天看點

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

繼續閱讀