jeecg-boot的shiro认证过程

本文档详细介绍了Jeecg-Boot中Shiro的配置过程,包括配置拦截路径、JWTFilter、自定义Realm以及相关Bean的初始化。通过配置ShiroFilter,设置了一系列无需权限验证的URL,并定义了自定义过滤器JWTFilter,用于接口登录验证。同时,文中还提到了Shiro的安全管理器配置和Realm的实现,确保系统认证的正常运行。
  1. shrio配置

说明:

  • 首先配置一系列拦截的路径
  • 配置jwtfilter 继承AuthenticatingFilter 作后面的接口登录验证
  • 自定义realm
  • 实例化常规bean

 

 

  1. 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、部分过滤器可指定参数,如permsroles
         */
       
    @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 存在安全漏洞泄露TOEKNdurid连接池也有)
           
    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);

    }

}

 

  1. 最后的登录接口

说明:

  • 常规通过查询数据库判断用户有效性
  • 通过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: 登录代码验证用户是否注销bugif条件永远为false
           
    LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
           
    queryWrapper.eq(SysUser::getUsername, username);
           
    SysUser sysUser = sysUserService.getOne(queryWrapper);

           
    //update-end-author:wangshuai date:20200601 for: 登录代码验证用户是否注销bugif条件永远为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()));
               
    //调用shirologout
               
    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 forissues#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 框架介绍 Jeecg-Boot 是一款基于 Spring Boot 和代码生成器的快速开发平台[^3]。该框架采用了前后端分离架构,集成了多个流行的技术栈: - 后端技术:Spring Boot, MyBatis, Shiro, JWT - 前端技术:Vue & Ant Design 通过这些集成,Jeecg-Boot 提供了一个完整的解决方案来加速企业级应用的开发过程。 #### 主要特点 1. **强大的代码生成功能** 使用内置的代码生成工具可以自动生成前端页面以及后端接口,极大地减少了手动编码的工作量。 2. **全面的安全机制** 集成 Apache Shiro 或其他安全组件实现用户认证授权管理,保障系统的安全性。 3. **灵活的数据访问层支持** 支持多种数据库连接方式,并且可以通过简单的配置完成数据源切换操作。 4. **丰富的插件生态** 提供了一系列开箱即用的功能模块(如定时任务调度、文件上传下载等),满足不同场景下的需求。 5. **易于扩展维护** 整体设计遵循微服务理念,在不影响现有业务的前提下方便后续功能迭代升级。 --- ### 使用教程概览 为了帮助开发者更好地理解和掌握 Jeecg-Boot 的核心概念及其实际应用场景,下面将给出一个简化的入门指南。 #### 准备工作 确保本地环境已经安装并配置好了 Java JDK 8 及以上版本、Maven 构建工具和 Git 版本控制系统。 #### 获取项目源码 可以从官方 GitHub 仓库克隆最新的稳定版分支到本地机器上进行学习研究: ```bash git clone https://github.com/jeecgboot/jeecg-boot.git cd jeecg-boot ``` #### 修改配置文件 首次启动前建议先调整 `src/main/resources/application-dev.yml` 中的相关参数以适配个人开发环境的要求[^2]: ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8 username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver ``` > 注意替换上述示例中的数据库地址、用户名密码等内容为你自己的 MySQL 实例信息。 #### 运行应用程序 执行 Maven 命令编译打包整个工程结构并将最终产物部署至内嵌 Tomcat 容器中运行起来: ```bash mvn clean spring-boot:run -Dspring.profiles.active=dev ``` 此时应该可以在浏览器里输入 http://localhost:8765 访问默认首页界面了。 #### 探索更多特性 随着对基础部分熟悉程度加深之后就可以尝试探索诸如 Swagger API 文档生成、Lombok 注解简化实体类定义等功能点[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值