前言:
緊接着 上一章 “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);
}
}
測試結果:
使用者名密碼正确:
使用者名或密碼錯誤: