前言:
紧接着 上一章 “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);
}
}
测试结果:
用户名密码正确:
用户名或密码错误: