- shrio配置
说明:
- 首先配置一系列拦截的路径
- 配置jwtfilter 继承AuthenticatingFilter 作后面的接口登录验证
- 自定义realm
- 实例化常规bean
- package com.hnlrkj.talentgift.config.shiro;
import com.hnlrkj.talentgift.config.shiro.filters.CustomShiroFilterFactoryBean;
import com.hnlrkj.talentgift.config.shiro.filters.JwtFilter;
import com.hnlrkj.talentgift.common.constant.CommonConstant;
import com.hnlrkj.talentgift.common.util.oConvertUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.crazycake.shiro.IRedisManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisClusterManager;
import org.crazycake.shiro.RedisManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.util.StringUtils;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import javax.annotation.Resource;
import javax.servlet.Filter;
import java.util.*;
/**
* @author:
* @date:
* @description: shiro 配置类
*/
@Slf4j
@Configuration
public class ShiroConfig {
@Value("${talentgift.shiro.excludeUrls}")
private String excludeUrls;
@Resource
LettuceConnectionFactory lettuceConnectionFactory;
@Autowired
private Environment env;
/**
* Filter Chain定义说明
*
* 1、一个URL可以配置多个Filter,使用逗号分隔
* 2、当设置多个过滤器时,全部验证通过,才视为通过
* 3、部分过滤器可指定参数,如perms,roles
*/
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
CustomShiroFilterFactoryBean shiroFilterFactoryBean = new CustomShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 拦截器
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
if(oConvertUtils.isNotEmpty(excludeUrls)){
String[] permissionUrl = excludeUrls.split(",");
for(String url : permissionUrl){
filterChainDefinitionMap.put(url,"anon");
}
}
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/sys/cas/client/validateLogin", "anon"); //cas验证登录
filterChainDefinitionMap.put("/sys/randomImage/**", "anon"); //登录验证码接口排除
filterChainDefinitionMap.put("/sys/checkCaptcha", "anon"); //登录验证码接口排除
filterChainDefinitionMap.put("/sys/login", "anon"); //登录接口排除
filterChainDefinitionMap.put("/sys/mLogin", "anon"); //登录接口排除
filterChainDefinitionMap.put("/sys/logout", "anon"); //登出接口排除
filterChainDefinitionMap.put("/sys/thirdLogin/**", "anon"); //第三方登录
filterChainDefinitionMap.put("/sys/getEncryptedString", "anon"); //获取加密串
filterChainDefinitionMap.put("/sys/sms", "anon");//短信验证码
filterChainDefinitionMap.put("/sys/phoneLogin", "anon");//手机登录
filterChainDefinitionMap.put("/sys/user/checkOnlyUser", "anon");//校验用户是否存在
filterChainDefinitionMap.put("/sys/user/register", "anon");//用户注册
filterChainDefinitionMap.put("/sys/user/querySysUser", "anon");//根据手机号获取用户信息
filterChainDefinitionMap.put("/sys/user/phoneVerification", "anon");//用户忘记密码验证手机号
filterChainDefinitionMap.put("/sys/user/passwordChange", "anon");//用户更改密码
filterChainDefinitionMap.put("/auth/2step-code", "anon");//登录验证码
filterChainDefinitionMap.put("/sys/common/static/**", "anon");//图片预览 &下载文件不限制token
filterChainDefinitionMap.put("/sys/common/pdf/**", "anon");//pdf预览
filterChainDefinitionMap.put("/generic/**", "anon");//pdf预览需要文件
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/doc.html", "anon");
filterChainDefinitionMap.put("/**/*.js", "anon");
filterChainDefinitionMap.put("/**/*.css", "anon");
filterChainDefinitionMap.put("/**/*.html", "anon");
filterChainDefinitionMap.put("/**/*.svg", "anon");
filterChainDefinitionMap.put("/**/*.pdf", "anon");
filterChainDefinitionMap.put("/**/*.jpg", "anon");
filterChainDefinitionMap.put("/**/*.png", "anon");
filterChainDefinitionMap.put("/**/*.ico", "anon");
filterChainDefinitionMap.put("/**/*.ttf", "anon");
filterChainDefinitionMap.put("/**/*.woff", "anon");
filterChainDefinitionMap.put("/**/*.woff2", "anon");
filterChainDefinitionMap.put("/druid/**", "anon");
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
filterChainDefinitionMap.put("/swagger**/**", "anon");
filterChainDefinitionMap.put("/webjars/**", "anon");
filterChainDefinitionMap.put("/v2/**", "anon");
//积木报表排除
filterChainDefinitionMap.put("/jmreport/**", "anon");
filterChainDefinitionMap.put("/**/*.js.map", "anon");
filterChainDefinitionMap.put("/**/*.css.map", "anon");
//大屏设计器排除
filterChainDefinitionMap.put("/bigscreen/**", "anon");
//测试示例
filterChainDefinitionMap.put("/test/bigScreen/**", "anon"); //大屏模板例子
//filterChainDefinitionMap.put("/test/jeecgDemo/rabbitMqClientTest/**", "anon"); //MQ测试
//filterChainDefinitionMap.put("/test/jeecgDemo/html", "anon"); //模板页面
//filterChainDefinitionMap.put("/test/jeecgDemo/redis/**", "anon"); //redis测试
//websocket排除
filterChainDefinitionMap.put("/websocket/**", "anon");//系统通知和公告
filterChainDefinitionMap.put("/newsWebsocket/**", "anon");//CMS模块
filterChainDefinitionMap.put("/vxeSocket/**", "anon");//JVxeTable无痕刷新示例
filterChainDefinitionMap.put("/eoaSocket/**","anon");//我的聊天
//性能监控 TODO 存在安全漏洞泄露TOEKN(durid连接池也有)
filterChainDefinitionMap.put("/actuator/**", "anon");
// 添加自己的过滤器并且取名为jwt
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
//如果cloudServer为空 则说明是单体 需要加载跨域配置
Object cloudServer = env.getProperty(CommonConstant.CLOUD_SERVER_KEY);
filterMap.put("jwt", new JwtFilter(cloudServer==null));
shiroFilterFactoryBean.setFilters(filterMap);
// <!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边
filterChainDefinitionMap.put("/**", "jwt");
// 未授权界面返回JSON
shiroFilterFactoryBean.setUnauthorizedUrl("/sys/common/403");
shiroFilterFactoryBean.setLoginUrl("/sys/common/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean("securityManager")
public DefaultWebSecurityManager securityManager(ShiroRealm myRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm);
/*
* 关闭shiro自带的session,详情见文档
* http://shiro.apache.org/session-management.html#SessionManagement-
* StatelessApplications%28Sessionless%29
*/
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
//自定义缓存实现,使用redis
securityManager.setCacheManager(redisCacheManager());
return securityManager;
}
/**
* 下面的代码是添加注解支持
* @return
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
/**
* 解决重复代理问题 github#994
* 添加前缀判断 不匹配 任何Advisor
*/
defaultAdvisorAutoProxyCreator.setUsePrefix(true);
defaultAdvisorAutoProxyCreator.setAdvisorBeanNamePrefix("_no_advisor");
return defaultAdvisorAutoProxyCreator;
}
@Bean
public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
/**
* cacheManager 缓存 redis实现
* 使用的是shiro-redis开源插件
*
* @return
*/
public RedisCacheManager redisCacheManager() {
log.info("===============(1)创建缓存管理器RedisCacheManager");
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
//redis中针对不同用户缓存(此处的id需要对应user实体中的id字段,用于唯一标识)
redisCacheManager.setPrincipalIdFieldName("id");
//用户权限信息缓存时间
redisCacheManager.setExpire(200000);
return redisCacheManager;
}
/**
* 配置shiro redisManager
* 使用的是shiro-redis开源插件
*
* @return
*/
@Bean
public IRedisManager redisManager() {
log.info("===============(2)创建RedisManager,连接Redis..");
IRedisManager manager;
// redis 单机支持,在集群为空,或者集群无机器时候使用 add by jzyadmin@163.com
if (lettuceConnectionFactory.getClusterConfiguration() == null || lettuceConnectionFactory.getClusterConfiguration().getClusterNodes().isEmpty()) {
RedisManager redisManager = new RedisManager();
redisManager.setHost(lettuceConnectionFactory.getHostName());
redisManager.setPort(lettuceConnectionFactory.getPort());
redisManager.setDatabase(lettuceConnectionFactory.getDatabase());
redisManager.setTimeout(0);
if (!StringUtils.isEmpty(lettuceConnectionFactory.getPassword())) {
redisManager.setPassword(lettuceConnectionFactory.getPassword());
}
manager = redisManager;
}else{
// redis集群支持,优先使用集群配置
RedisClusterManager redisManager = new RedisClusterManager();
Set<HostAndPort> portSet = new HashSet<>();
lettuceConnectionFactory.getClusterConfiguration().getClusterNodes().forEach(node -> portSet.add(new HostAndPort(node.getHost() , node.getPort())));
JedisCluster jedisCluster = new JedisCluster(portSet);
redisManager.setJedisCluster(jedisCluster);
manager = redisManager;
}
return manager;
}
}
2. jwtFilter
说明
- 该过滤器主要是通过对前端传的head中的token进行用户身份的验证
- getSubject(request, response).login(jwtToken); 交给realm认证
package com.hnlrkj.talentgift.config.shiro.filters;
import com.hnlrkj.talentgift.common.constant.CommonConstant;
import com.hnlrkj.talentgift.config.mybatis.TenantContext;
import com.hnlrkj.talentgift.config.shiro.JwtToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description: 鉴权登录拦截器
* @Author:
* @Date:
**/
@Slf4j
public class JwtFilter extends BasicHttpAuthenticationFilter {
private boolean allowOrigin = true;
public JwtFilter(){}
public JwtFilter(boolean allowOrigin){
this.allowOrigin = allowOrigin;
}
/**
* 执行登录认证
*
* @param request
* @param response
* @param mappedValue
* @return
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
try {
executeLogin(request, response);
return true;
} catch (Exception e) {
throw new AuthenticationException("Token失效,请重新登录", e);
}
}
/**
*
*/
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader(CommonConstant.X_ACCESS_TOKEN);
// update-begin--Author:lvdandan Date:20210105 for:JT-355 OA聊天添加token验证,获取token参数
if(token == null){
token = httpServletRequest.getParameter("token");
}
// update-end--Author:lvdandan Date:20210105 for:JT-355 OA聊天添加token验证,获取token参数
JwtToken jwtToken = new JwtToken(token);
// 提交给realm进行登入,如果错误他会抛出异常并被捕获
getSubject(request, response).login(jwtToken);
// 如果没有抛出异常则代表登入成功,返回true
return true;
}
/**
* 对跨域提供支持
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
if(allowOrigin){
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
//update-begin-author:scott date:20200907 for:issues/I1TAAP 前后端分离,shiro过滤器配置引起的跨域问题
// 是否允许发送Cookie,默认Cookie不包括在CORS请求之中。设为true时,表示服务器允许Cookie包含在请求中。
httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
//update-end-author:scott date:20200907 for:issues/I1TAAP 前后端分离,shiro过滤器配置引起的跨域问题
}
// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
//update-begin-author:taoyan date:20200708 for:多租户用到
String tenant_id = httpServletRequest.getHeader(CommonConstant.TENANT_ID);
TenantContext.setTenant(tenant_id);
//update-end-author:taoyan date:20200708 for:多租户用到
return super.preHandle(request, response);
}
}
- 最后的登录接口
说明:
- 常规通过查询数据库判断用户有效性
- 通过jwt工具类,签发token,返回到前端
- package com.hnlrkj.talentgift.modules.system.controller;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.exceptions.ClientException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hnlrkj.talentgift.common.util.*;
import com.hnlrkj.talentgift.common.util.RedisUtil;
import com.hnlrkj.talentgift.common.api.vo.Result;
import com.hnlrkj.talentgift.common.constant.CacheConstant;
import com.hnlrkj.talentgift.common.constant.CommonConstant;
import com.hnlrkj.talentgift.common.system.api.ISysBaseAPI;
import com.hnlrkj.talentgift.common.system.util.JwtUtil;
import com.hnlrkj.talentgift.common.system.vo.LoginUser;
import com.hnlrkj.talentgift.common.util.encryption.EncryptedString;
import com.hnlrkj.talentgift.modules.base.service.BaseCommonService;
import com.hnlrkj.talentgift.modules.system.entity.SysDepart;
import com.hnlrkj.talentgift.modules.system.entity.SysPermission;
import com.hnlrkj.talentgift.modules.system.entity.SysRole;
import com.hnlrkj.talentgift.modules.system.entity.SysUser;
import com.hnlrkj.talentgift.modules.system.model.SysLoginModel;
import com.hnlrkj.talentgift.modules.system.service.*;
import com.hnlrkj.talentgift.modules.system.util.PermissionDataUtil;
import com.hnlrkj.talentgift.modules.system.util.RandImageUtil;
import com.hnlrkj.util.core.CheckUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
/**
* @Author scott
* @since 2018-12-17
*/
@RestController
@RequestMapping("/sys")
@Api(tags = "用户登录")
@Slf4j
public class LoginController {
@Autowired
private ISysUserService sysUserService;
@Autowired
private ISysBaseAPI sysBaseAPI;
@Autowired
private ISysLogService logService;
@Autowired
private RedisUtil redisUtil;
@Autowired
private ISysDepartService sysDepartService;
@Autowired
private ISysDictService sysDictService;
@Resource
private BaseCommonService baseCommonService;
@Autowired
private ISysRoleService sysRoleService;
@Autowired
private ISysPermissionService sysPermissionService;
private static final String BASE_CHECK_CODES = "qwertyuiplkjhgfdsazxcvbnmQWERTYUPLKJHGFDSAZXCVBNM1234567890";
@ApiOperation("登录接口")
@RequestMapping(value = "/login", method = RequestMethod.POST)
public Result<JSONObject> login(SysLoginModel sysLoginModel) {
Result<JSONObject> result = new Result<JSONObject>();
String username = sysLoginModel.getUsername();
String password = sysLoginModel.getPassword();
//update-begin--Author:scott Date:20190805 for:暂时注释掉密码加密逻辑,有点问题
//前端密码加密,后端进行密码解密
//password = AesEncryptUtil.desEncrypt(sysLoginModel.getPassword().replaceAll("%2B", "\\+")).trim();//密码解密
//update-begin--Author:scott Date:20190805 for:暂时注释掉密码加密逻辑,有点问题
//update-begin-author:taoyan date:20190828 for:校验验证码
String captcha = sysLoginModel.getCaptcha();
if (captcha == null) {
result.error500("验证码无效");
return result;
}
String lowerCaseCaptcha = captcha.toLowerCase();
String realKey = MD5Util.MD5Encode(lowerCaseCaptcha + sysLoginModel.getCheckKey(), "utf-8");
Object checkCode = redisUtil.get(realKey);
//当进入登录页时,有一定几率出现验证码错误 #1714
if (checkCode == null || !checkCode.toString().equals(lowerCaseCaptcha)) {
result.error500("验证码错误");
return result;
}
//update-end-author:taoyan date:20190828 for:校验验证码
//1. 校验用户是否有效
//update-begin-author:wangshuai date:20200601 for: 登录代码验证用户是否注销bug,if条件永远为false
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUser::getUsername, username);
SysUser sysUser = sysUserService.getOne(queryWrapper);
//update-end-author:wangshuai date:20200601 for: 登录代码验证用户是否注销bug,if条件永远为false
result = sysUserService.checkUserIsEffective(sysUser);
if (!result.isSuccess()) {
return result;
}
//2. 校验用户名或密码是否正确
String userpassword = PasswordUtil.encrypt(null, password, sysUser.getSalt());
String syspassword = sysUser.getPassword();
if (!syspassword.equals(userpassword)) {
result.error500("用户名或密码错误");
return result;
}
//用户登录信息
userInfo(sysUser, result);
//update-begin--Author:liusq Date:20210126 for:登录成功,删除redis中的验证码
redisUtil.del(realKey);
//update-begin--Author:liusq Date:20210126 for:登录成功,删除redis中的验证码
LoginUser loginUser = new LoginUser();
BeanUtils.copyProperties(sysUser, loginUser);
//返回用户角色
List<SysRole> role = sysRoleService.getRoleByUserId(sysUser.getId());
if(null!=role&&role.size()>0){
StringBuffer buffer = new StringBuffer();
role.forEach(s->{
if(CheckUtil.notEmpty(s)&&null!=s){
if(null!=buffer&&!"".equals(buffer.toString())){
buffer.append(",");
}
buffer.append(s.getRoleCode());
}
});
loginUser.setRoles(buffer.toString());
result.getResult().put("roles",loginUser.getRoles());
boolean home = false;
//返回用户角色
List<SysRole> sRole = sysRoleService.getRoleByUserId(loginUser.getId());
SysPermission indexMenu = new SysPermission();
if(null!=sRole){
indexMenu = sysPermissionService.queryRoleCode("首页",sRole.get(0).getRoleCode());
if(null!=indexMenu){
home = true;
}
}
//判断是否有首页
if(home){
result.getResult().put("url",indexMenu.getUrl());
}else{
result.getResult().put("url",null);
}
}
baseCommonService.addLog("用户名: " + username + ",登录成功!", CommonConstant.LOG_TYPE_1, null, loginUser);
return result;
}
/**
* 退出登录
*
* @param request
* @param response
* @return
*/
@RequestMapping(value = "/logout")
public Result<Object> logout(HttpServletRequest request, HttpServletResponse response) {
//用户退出逻辑
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
if (oConvertUtils.isEmpty(token)) {
return Result.error("退出登录失败!");
}
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseAPI.getUserByName(username);
if (sysUser != null) {
//update-begin--Author:wangshuai Date:20200714 for:登出日志没有记录人员
baseCommonService.addLog("用户名: " + sysUser.getRealname() + ",退出成功!", CommonConstant.LOG_TYPE_1, null, sysUser);
//update-end--Author:wangshuai Date:20200714 for:登出日志没有记录人员
log.info(" 用户名: " + sysUser.getRealname() + ",退出成功! ");
//清空用户登录Token缓存
redisUtil.del(CommonConstant.PREFIX_USER_TOKEN + token);
//清空用户登录Shiro权限缓存
redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + sysUser.getId());
//清空用户的缓存信息(包括部门信息),例如sys:cache:user::<username>
redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, sysUser.getUsername()));
//调用shiro的logout
SecurityUtils.getSubject().logout();
return Result.ok("退出登录成功!");
} else {
return Result.error("Token无效!");
}
}
/**
* 获取访问量
*
* @return
*/
@GetMapping("loginfo")
public Result<JSONObject> loginfo() {
Result<JSONObject> result = new Result<JSONObject>();
JSONObject obj = new JSONObject();
//update-begin--Author:zhangweijian Date:20190428 for:传入开始时间,结束时间参数
// 获取一天的开始和结束时间
Calendar calendar = new GregorianCalendar();
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
Date dayStart = calendar.getTime();
calendar.add(Calendar.DATE, 1);
Date dayEnd = calendar.getTime();
// 获取系统访问记录
Long totalVisitCount = logService.findTotalVisitCount();
obj.put("totalVisitCount", totalVisitCount);
Long todayVisitCount = logService.findTodayVisitCount(dayStart, dayEnd);
obj.put("todayVisitCount", todayVisitCount);
Long todayIp = logService.findTodayIp(dayStart, dayEnd);
//update-end--Author:zhangweijian Date:20190428 for:传入开始时间,结束时间参数
obj.put("todayIp", todayIp);
result.setResult(obj);
result.success("登录成功");
return result;
}
/**
* 获取访问量
*
* @return
*/
@GetMapping("visitInfo")
public Result<List<Map<String, Object>>> visitInfo() {
Result<List<Map<String, Object>>> result = new Result<List<Map<String, Object>>>();
Calendar calendar = new GregorianCalendar();
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
calendar.add(Calendar.DAY_OF_MONTH, 1);
Date dayEnd = calendar.getTime();
calendar.add(Calendar.DAY_OF_MONTH, -7);
Date dayStart = calendar.getTime();
List<Map<String, Object>> list = logService.findVisitCount(dayStart, dayEnd);
result.setResult(oConvertUtils.toLowerCasePageList(list));
return result;
}
/**
* 登陆成功选择用户当前部门
*
* @param user
* @return
*/
@RequestMapping(value = "/selectDepart", method = RequestMethod.PUT)
public Result<JSONObject> selectDepart(SysUser user) {
Result<JSONObject> result = new Result<JSONObject>();
String username = user.getUsername();
if (oConvertUtils.isEmpty(username)) {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
username = sysUser.getUsername();
}
String orgCode = user.getOrgCode();
this.sysUserService.updateUserDepart(username, orgCode);
SysUser sysUser = sysUserService.getUserByName(username);
JSONObject obj = new JSONObject();
obj.put("userInfo", sysUser);
result.setResult(obj);
return result;
}
/**
* 短信登录接口
*
* @param jsonObject
* @return
*/
@PostMapping(value = "/sms")
public Result<String> sms(JSONObject jsonObject) {
Result<String> result = new Result<String>();
String mobile = jsonObject.get("mobile").toString();
//手机号模式 登录模式: "2" 注册模式: "1"
String smsmode = jsonObject.get("smsmode").toString();
log.info(mobile);
if (oConvertUtils.isEmpty(mobile)) {
result.setMessage("手机号不允许为空!");
result.setSuccess(false);
return result;
}
Object object = redisUtil.get(mobile);
if (object != null) {
result.setMessage("验证码10分钟内,仍然有效!");
result.setSuccess(false);
return result;
}
//随机数
String captcha = RandomUtil.randomNumbers(6);
JSONObject obj = new JSONObject();
obj.put("code", captcha);
try {
boolean b = false;
//注册模板
if (CommonConstant.SMS_TPL_TYPE_1.equals(smsmode)) {
SysUser sysUser = sysUserService.getUserByPhone(mobile);
if (sysUser != null) {
result.error500(" 手机号已经注册,请直接登录!");
baseCommonService.addLog("手机号已经注册,请直接登录!", CommonConstant.LOG_TYPE_1, null);
return result;
}
b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.REGISTER_TEMPLATE_CODE);
} else {
//登录模式,校验用户有效性
SysUser sysUser = sysUserService.getUserByPhone(mobile);
result = sysUserService.checkUserIsEffective(sysUser);
if (!result.isSuccess()) {
String message = result.getMessage();
if ("该用户不存在,请注册".equals(message)) {
result.error500("该用户不存在或未绑定手机号");
}
return result;
}
/**
* smsmode 短信模板方式 0 .登录模板、1.注册模板、2.忘记密码模板
*/
if (CommonConstant.SMS_TPL_TYPE_0.equals(smsmode)) {
//登录模板
b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.LOGIN_TEMPLATE_CODE);
} else if (CommonConstant.SMS_TPL_TYPE_2.equals(smsmode)) {
//忘记密码模板
b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.FORGET_PASSWORD_TEMPLATE_CODE);
}
}
if (b == false) {
result.setMessage("短信验证码发送失败,请稍后重试");
result.setSuccess(false);
return result;
}
//验证码10分钟内有效
redisUtil.set(mobile, captcha, 600);
//update-begin--Author:scott Date:20190812 for:issues#391
//result.setResult(captcha);
//update-end--Author:scott Date:20190812 for:issues#391
result.setSuccess(true);
} catch (ClientException e) {
e.printStackTrace();
result.error500(" 短信接口未配置,请联系管理员!");
return result;
}
return result;
}
/**
* 手机号登录接口
*
* @param jsonObject
* @return
*/
@ApiOperation("手机号登录接口")
@PostMapping("/phoneLogin")
public Result<JSONObject> phoneLogin(JSONObject jsonObject) {
Result<JSONObject> result = new Result<JSONObject>();
String phone = jsonObject.getString("mobile");
//校验用户有效性
SysUser sysUser = sysUserService.getUserByPhone(phone);
result = sysUserService.checkUserIsEffective(sysUser);
if (!result.isSuccess()) {
return result;
}
String smscode = jsonObject.getString("captcha");
Object code = redisUtil.get(phone);
if (!smscode.equals(code)) {
result.setMessage("手机验证码错误");
return result;
}
//用户信息
userInfo(sysUser, result);
//添加日志
baseCommonService.addLog("用户名: " + sysUser.getUsername() + ",登录成功!", CommonConstant.LOG_TYPE_1, null);
return result;
}
/**
* 用户信息
*
* @param sysUser
* @param result
* @return
*/
private Result<JSONObject> userInfo(SysUser sysUser, Result<JSONObject> result) {
String syspassword = sysUser.getPassword();
String username = sysUser.getUsername();
// 生成token
String token = JwtUtil.sign(username, syspassword);
// 设置token缓存有效时间
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);
// 获取用户部门信息
JSONObject obj = new JSONObject();
List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId());
obj.put("departs", departs);
if (departs == null || departs.size() == 0) {
obj.put("multi_depart", 0);
} else if (departs.size() == 1) {
sysUserService.updateUserDepart(username, departs.get(0).getOrgCode());
obj.put("multi_depart", 1);
} else {
//查询当前是否有登录部门
// update-begin--Author:wangshuai Date:20200805 for:如果用戶为选择部门,数据库为存在上一次登录部门,则取一条存进去
SysUser sysUserById = sysUserService.getById(sysUser.getId());
if (oConvertUtils.isEmpty(sysUserById.getOrgCode())) {
sysUserService.updateUserDepart(username, departs.get(0).getOrgCode());
}
// update-end--Author:wangshuai Date:20200805 for:如果用戶为选择部门,数据库为存在上一次登录部门,则取一条存进去
obj.put("multi_depart", 2);
}
obj.put("token", token);
obj.put("userInfo", sysUser);
obj.put("sysAllDictItems", sysDictService.queryAllDictItems());
result.setResult(obj);
result.success("登录成功");
return result;
}
/**
* 获取加密字符串
*
* @return
*/
@GetMapping(value = "/getEncryptedString")
public Result<Map<String, String>> getEncryptedString() {
Result<Map<String, String>> result = new Result<Map<String, String>>();
Map<String, String> map = new HashMap<String, String>();
map.put("key", EncryptedString.key);
map.put("iv", EncryptedString.iv);
result.setResult(map);
return result;
}
/**
* 后台生成图形验证码 :有效
*
* @param response
* @param key
*/
@ApiOperation("获取验证码")
@GetMapping(value = "/randomImage/{key}")
public Result<String> randomImage(HttpServletResponse response, @PathVariable String key) {
Result<String> res = new Result<String>();
try {
String code = RandomUtil.randomString(BASE_CHECK_CODES, 4);
String lowerCaseCode = code.toLowerCase();
String realKey = MD5Util.MD5Encode(lowerCaseCode + key, "utf-8");
redisUtil.set(realKey, lowerCaseCode, 60);
String base64 = RandImageUtil.generate(code);
res.setSuccess(true);
res.setResult(base64);
} catch (Exception e) {
res.error500("获取验证码出错" + e.getMessage());
e.printStackTrace();
}
return res;
}
/**
* app登录
*
* @param sysLoginModel
* @return
* @throws Exception
*/
@RequestMapping(value = "/mLogin", method = RequestMethod.POST)
public Result<JSONObject> mLogin(SysLoginModel sysLoginModel) throws Exception {
Result<JSONObject> result = new Result<JSONObject>();
String username = sysLoginModel.getUsername();
String password = sysLoginModel.getPassword();
//1. 校验用户是否有效
SysUser sysUser = sysUserService.getUserByName(username);
result = sysUserService.checkUserIsEffective(sysUser);
if (!result.isSuccess()) {
return result;
}
//2. 校验用户名或密码是否正确
String userpassword = PasswordUtil.encrypt(null, password, sysUser.getSalt());
String syspassword = sysUser.getPassword();
if (!syspassword.equals(userpassword)) {
result.error500("用户名或密码错误");
return result;
}
String orgCode = sysUser.getOrgCode();
if (oConvertUtils.isEmpty(orgCode)) {
//如果当前用户无选择部门 查看部门关联信息
List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId());
if (departs == null || departs.size() == 0) {
result.error500("用户暂未归属部门,不可登录!");
return result;
}
orgCode = departs.get(0).getOrgCode();
sysUser.setOrgCode(orgCode);
this.sysUserService.updateUserDepart(username, orgCode);
}
JSONObject obj = new JSONObject();
//用户登录信息
obj.put("userInfo", sysUser);
// 生成token
String token = JwtUtil.sign(username, syspassword);
// 设置超时时间
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);
//token 信息
obj.put("token", token);
result.setResult(obj);
result.setSuccess(true);
result.setCode(200);
baseCommonService.addLog("用户名: " + username + ",登录成功[移动端]!", CommonConstant.LOG_TYPE_1, null);
return result;
}
/**
* 图形验证码
*
* @param sysLoginModel
* @return
*/
@RequestMapping(value = "/checkCaptcha", method = RequestMethod.POST)
public Result<?> checkCaptcha(SysLoginModel sysLoginModel) {
String captcha = sysLoginModel.getCaptcha();
String checkKey = sysLoginModel.getCheckKey();
if (captcha == null) {
return Result.error("验证码无效");
}
String lowerCaseCaptcha = captcha.toLowerCase();
String realKey = MD5Util.MD5Encode(lowerCaseCaptcha + checkKey, "utf-8");
Object checkCode = redisUtil.get(realKey);
if (checkCode == null || !checkCode.equals(lowerCaseCaptcha)) {
return Result.error("验证码错误");
}
return Result.ok();
} -
/** * 用户信息 * * @param sysUser * @param result * @return */ private Result<JSONObject> userInfo(SysUser sysUser, Result<JSONObject> result) { String syspassword = sysUser.getPassword(); String username = sysUser.getUsername(); // 生成token String token = JwtUtil.sign(username, syspassword); // 设置token缓存有效时间 redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token); redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000); // 获取用户部门信息 JSONObject obj = new JSONObject(); List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId()); obj.put("departs", departs); if (departs == null || departs.size() == 0) { obj.put("multi_depart", 0); } else if (departs.size() == 1) { sysUserService.updateUserDepart(username, departs.get(0).getOrgCode()); obj.put("multi_depart", 1); } else { //查询当前是否有登录部门 // update-begin--Author:wangshuai Date:20200805 for:如果用戶为选择部门,数据库为存在上一次登录部门,则取一条存进去 SysUser sysUserById = sysUserService.getById(sysUser.getId()); if (oConvertUtils.isEmpty(sysUserById.getOrgCode())) { sysUserService.updateUserDepart(username, departs.get(0).getOrgCode()); } // update-end--Author:wangshuai Date:20200805 for:如果用戶为选择部门,数据库为存在上一次登录部门,则取一条存进去 obj.put("multi_depart", 2); } obj.put("token", token); obj.put("userInfo", sysUser); obj.put("sysAllDictItems", sysDictService.queryAllDictItems()); result.setResult(obj); result.success("登录成功"); return result; }
}
本文档详细介绍了Jeecg-Boot中Shiro的配置过程,包括配置拦截路径、JWTFilter、自定义Realm以及相关Bean的初始化。通过配置ShiroFilter,设置了一系列无需权限验证的URL,并定义了自定义过滤器JWTFilter,用于接口登录验证。同时,文中还提到了Shiro的安全管理器配置和Realm的实现,确保系统认证的正常运行。
2674

被折叠的 条评论
为什么被折叠?



