3 Commits 3839a4ca65 ... 676637fef0

Author SHA1 Message Date
  danch 676637fef0 文章状态随着文章分类状态的改变而改变,父文章分类的状态改变也会改变子文章分类 3 weeks ago
  danch 7d452eb453 文章管理改造hou'duan 3 weeks ago
  danch 49bc817f51 rememberme功能后端 3 weeks ago

+ 25 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java

@@ -2,13 +2,20 @@ package org.jeecg.config.shiro;
 
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
 import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
 import org.apache.shiro.mgt.DefaultSubjectDAO;
+import org.apache.shiro.mgt.RememberMeManager;
 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.CookieRememberMeManager;
 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
+import org.apache.shiro.web.servlet.Cookie;
+import org.apache.shiro.web.servlet.SimpleCookie;
+import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
+import org.apache.shiro.web.session.mgt.WebSessionManager;
 import org.crazycake.shiro.IRedisManager;
 import org.crazycake.shiro.RedisCacheManager;
 import org.crazycake.shiro.RedisClusterManager;
@@ -50,6 +57,23 @@ public class ShiroConfig {
     @Resource
     private JeecgBaseConfig jeecgBaseConfig;
 
+    // 配置 RememberMeManager
+    @Bean
+    public RememberMeManager rememberMeManager() {
+        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
+        rememberMeManager.setCookie(rememberMeCookie());  // 配置 RememberMeCookie
+        return rememberMeManager;
+    }
+
+    // 配置 RememberMeCookie
+    @Bean
+    public SimpleCookie rememberMeCookie() {
+        SimpleCookie rememberMeCookie = new SimpleCookie("rememberMe");
+        rememberMeCookie.setMaxAge(30 * 24 * 60 * 60);  // 设置最大有效期为 30 天
+        rememberMeCookie.setHttpOnly(true);  // 设置为 HttpOnly
+        return rememberMeCookie;
+    }
+
     /**
      * Filter Chain定义说明
      *
@@ -177,6 +201,7 @@ public class ShiroConfig {
          * http://shiro.apache.org/session-management.html#SessionManagement-
          * StatelessApplications%28Sessionless%29
          */
+        securityManager.setRememberMeManager(rememberMeManager());
         DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
         DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
         defaultSessionStorageEvaluator.setSessionStorageEnabled(false);

+ 11 - 5
jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroRealm.java

@@ -1,10 +1,7 @@
 package org.jeecg.config.shiro;
 
 import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.authc.AuthenticationException;
-import org.apache.shiro.authc.AuthenticationInfo;
-import org.apache.shiro.authc.AuthenticationToken;
-import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authc.*;
 import org.apache.shiro.authz.AuthorizationInfo;
 import org.apache.shiro.authz.SimpleAuthorizationInfo;
 import org.apache.shiro.realm.AuthorizingRealm;
@@ -48,7 +45,7 @@ public class ShiroRealm extends AuthorizingRealm {
      */
     @Override
     public boolean supports(AuthenticationToken token) {
-        return token instanceof JwtToken;
+        return token instanceof JwtToken || token instanceof UsernamePasswordToken;
     }
 
     /**
@@ -92,6 +89,15 @@ public class ShiroRealm extends AuthorizingRealm {
     @Override
     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
         log.debug("===============Shiro身份认证开始============doGetAuthenticationInfo==========");
+        if (auth instanceof UsernamePasswordToken) {
+            UsernamePasswordToken upToken = (UsernamePasswordToken) auth;
+            String username = upToken.getUsername();
+            char[] password = upToken.getPassword();
+              if(username==null||password==null){
+                  throw new AuthenticationException("用户名或密码不能为空");
+              }
+            return new SimpleAuthenticationInfo(username, password, getName());
+        }
         String token = (String) auth.getCredentials();
         if (token == null) {
             HttpServletRequest req = SpringContextUtils.getHttpServletRequest();

+ 11 - 0
jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/filters/JwtFilter.java

@@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
 
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
@@ -46,6 +47,16 @@ public class JwtFilter extends BasicHttpAuthenticationFilter {
      */
     @Override
     protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
+        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+        // 优先判断是否存在 RememberMe Cookie
+        if (httpServletRequest.getCookies() != null) {
+            for (Cookie cookie : httpServletRequest.getCookies()) {
+                if ("rememberMe".equals(cookie.getName())) {
+                    // 如果 Cookie 存在,则不进行 JWT 校验,直接通过
+                    return true;
+                }
+            }
+        }
         try {
             executeLogin(request, response);
             return true;

+ 55 - 1
jeecg-module-kms/src/main/java/org/jeecg/modules/kms/bas/service/impl/CategoryServiceImpl.java

@@ -11,9 +11,11 @@ import org.jeecg.modules.kms.bas.entity.Category;
 import org.jeecg.modules.kms.bas.mapper.KmsCategoryMapper;
 import org.jeecg.modules.kms.bas.service.ICategoryService;
 import org.jeecg.modules.kms.knowledge.entity.Article;
+import org.jeecg.modules.kms.knowledge.mapper.ArticleMapper;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import javax.annotation.Resource;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -26,6 +28,9 @@ import java.util.stream.Collectors;
 @Service
 public class CategoryServiceImpl extends ServiceImpl<KmsCategoryMapper, Category> implements ICategoryService {
 
+    @Resource
+    private ArticleMapper articleMapper;
+
     @Override
     public void addCategory(Category category) {
         //新增时设置hasChild为0
@@ -60,9 +65,58 @@ public class CategoryServiceImpl extends ServiceImpl<KmsCategoryMapper, Category
                 baseMapper.updateTreeNodeStatus(category.getPid(), ICategoryService.HASCHILD);
             }
         }
-        baseMapper.updateById(category);
+        // 文章分类状态改变
+        String oldIsPublic = entity.getIsPublic();
+        String newIsPublic = category.getIsPublic();
+        int i = baseMapper.updateById(category);
+        // 更新成功后改变本身及其子结点的文章状态
+        if (!oldIsPublic.equals(newIsPublic) && i > 0) {
+            updateArticleIsPublicByCategoryId(category);
+        }
+    }
+
+    /**
+     * 更新父节点及其文章状态 、 更新子节点及其文章状态
+     *
+     * @param category
+     */
+    private void updateArticleIsPublicByCategoryId(Category category) {
+        List<Category> categories = new ArrayList<>();
+        findAllChildrenIds(category, categories);
+        // 更新子节点的is_public状态
+        if (categories.isEmpty() || categories.size() == 0) {
+            return;
+        }
+        categories.forEach(c -> c.setIsPublic(category.getIsPublic()));
+        boolean updateBatchByIdStatus = this.updateBatchById(categories);
+        //更新失败,抛出异常
+        if (!updateBatchByIdStatus) {
+            throw new JeecgBootException("更新失败");
+        }
+        // 更新article的is_public状态
+        List<String> categoryIds = categories.stream()
+                .map(Category::getId)
+                .collect(Collectors.toList());
+        articleMapper.updateIsPublicByCategoryIds(categoryIds, category.getIsPublic());
     }
 
+    /**
+     * 递归获取所有子节点
+     *
+     * @param category
+     * @param categories
+     */
+    private void findAllChildrenIds(Category category, List<Category> categories) {
+        categories.add(category);
+        QueryWrapper<Category> queryWrapper = new QueryWrapper<>();
+        List<Category> children = this.list(queryWrapper.eq("pid", category.getId()));
+        for (Category child : children) {
+            findAllChildrenIds(child, categories);
+        }
+
+    }
+
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void deleteCategory(String id) throws JeecgBootException {

+ 31 - 0
jeecg-module-kms/src/main/java/org/jeecg/modules/kms/knowledge/controller/ArticleController.java

@@ -86,7 +86,38 @@ public class ArticleController extends JeecgController<Article, IArticleService>
         }
         return Result.OK(pageList);
     }
+    /**
+     * 分页列表查询
+     *
+     * @param article
+     * @param pageNo
+     * @param pageSize
+     * @param req
+     * @return
+     */
+    //@AutoLog(value = "知识管理-文章-分页列表查询")
+    @ApiOperation(value = "知识管理-文章-文章管理", notes = "知识管理-文章-文章管理")
+    @GetMapping(value = "/listManage")
+    @PermissionData(pageComponent = "/kms/knowledge/article/ArticleManage")
+    public Result<IPage<Article>> queryPagelistManage(Article article,
+                                                @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
+                                                @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
+                                                HttpServletRequest req) {
+        QueryWrapper<Article> queryWrapper = QueryGenerator.initQueryWrapper(article, req.getParameterMap());
+        Page<Article> page = new Page<Article>(pageNo, pageSize);
+        IPage<Article> pageList = articleService.queryListWithPermission(page, queryWrapper);
+        // 获取CategoryId,如果CategoryId删除就获取他的父id以此类推
+        for (Article articleFor : pageList.getRecords()) {
+            if (articleFor.getCategoryId() != null) {
 
+                List<Category> categories = categoryService.queryParentNodeById(articleFor.getCategoryId());
+                if (categories.size() > 0) {
+                    articleFor.setCategoryId(categories.get(0).getId());
+                }
+            }
+        }
+        return Result.OK(pageList);
+    }
     @ApiOperation(value = "知识管理-文章-分页列表查询", notes = "知识管理-文章-分页列表查询")
     @PostMapping(value = "/queryPageListByFilters")
     @PermissionData(pageComponent = "kms/knowledge/article/ArticleList")

+ 3 - 0
jeecg-module-kms/src/main/java/org/jeecg/modules/kms/knowledge/entity/Article.java

@@ -42,6 +42,7 @@ public class Article implements Serializable {
      * 创建人
      */
     @ApiModelProperty(value = "创建人")
+    @Dict(dictTable = "sys_user", dicCode = "username", dicText = "realname")
     private String createBy;
     /**
      * 创建日期
@@ -54,6 +55,7 @@ public class Article implements Serializable {
      * 更新人
      */
     @ApiModelProperty(value = "更新人")
+    @Dict(dictTable = "sys_user", dicCode = "username", dicText = "realname")
     private String updateBy;
     /**
      * 更新日期
@@ -66,6 +68,7 @@ public class Article implements Serializable {
      * 所属部门
      */
     @ApiModelProperty(value = "所属部门")
+    @Dict(dictTable = "sys_depart", dicCode = "org_code", dicText = "depart_name")
     private String sysOrgCode;
     /**
      * 标题

+ 10 - 0
jeecg-module-kms/src/main/java/org/jeecg/modules/kms/knowledge/mapper/ArticleMapper.java

@@ -1,12 +1,17 @@
 package org.jeecg.modules.kms.knowledge.mapper;
 
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.apache.ibatis.annotations.Param;
 import org.jeecg.modules.kms.knowledge.entity.Article;
 import org.jeecg.modules.kms.knowledge.param.ArticleFilterParam;
 
+import java.util.List;
+
 /**
  * @Description: 知识管理-文章
  * @Author: jeecg-boot
@@ -16,4 +21,9 @@ import org.jeecg.modules.kms.knowledge.param.ArticleFilterParam;
 public interface ArticleMapper extends BaseMapper<Article> {
 
     IPage<Article> queryPageListByFilters(@Param("pages") IPage<Article> pages, @Param("filterParam") ArticleFilterParam filterParam, @Param("sql") String sql);
+
+    IPage<Article> queryListWithPermission(@Param("page") Page<Article> page, @Param("sql") String sql,@Param(Constants.WRAPPER)QueryWrapper<Article> queryWrapper);
+
+    void updateIsPublicByCategoryIds(@Param("categoryIds") List<String> categoryIds, @Param("isPublic") String isPublic);
+
 }

+ 34 - 12
jeecg-module-kms/src/main/java/org/jeecg/modules/kms/knowledge/mapper/xml/ArticleMapper.xml

@@ -26,16 +26,38 @@
             AND t0.title like CONCAT(CONCAT('%', #{filterParam.keyWords}), '%')
         </if>
     </select>
-<!--    <select id="queryPageListByFilters" resultType="org.jeecg.modules.kms.knowledge.entity.Article">-->
-<!--            select distinct t0.* from kms_knowledge_article t0-->
-<!--                              inner join kms_article_category_r t1 on t0.id = t1.article_id-->
-<!--            where t0.status = 2 AND t0.is_public != '1'-->
-<!--            <if test="filterParam.categoryId != null and filterParam.categoryId != ''">-->
-<!--                AND t1.category_id = #{filterParam.categoryId}-->
-<!--            </if>-->
-<!--            <if test="filterParam.keyWords != null and filterParam.keyWords != ''">-->
-<!--                AND t0.title like CONCAT(CONCAT('%', #{filterParam.keyWords}), '%')-->
-<!--            </if>-->
-<!--            ${sql}-->
-<!--    </select>-->
+    <!--    <select id="queryPageListByFilters" resultType="org.jeecg.modules.kms.knowledge.entity.Article">-->
+    <!--            select distinct t0.* from kms_knowledge_article t0-->
+    <!--                              inner join kms_article_category_r t1 on t0.id = t1.article_id-->
+    <!--            where t0.status = 2 AND t0.is_public != '1'-->
+    <!--            <if test="filterParam.categoryId != null and filterParam.categoryId != ''">-->
+    <!--                AND t1.category_id = #{filterParam.categoryId}-->
+    <!--            </if>-->
+    <!--            <if test="filterParam.keyWords != null and filterParam.keyWords != ''">-->
+    <!--                AND t0.title like CONCAT(CONCAT('%', #{filterParam.keyWords}), '%')-->
+    <!--            </if>-->
+    <!--            ${sql}-->
+    <!--    </select>-->
+    <select id="queryListWithPermission" resultType="org.jeecg.modules.kms.knowledge.entity.Article">
+        select t0.*
+        from (select t0.*
+              from kms_knowledge_article t0
+              where t0.is_public != '1' ${sql}
+              UNION
+              select t0.*
+              from kms_knowledge_article t0
+              where t0.is_public != '0') t0
+            ${ew.customSqlSegment}
+    </select>
+    <update id="updateIsPublicByCategoryIds">
+        <if test="categoryIds != null and !categoryIds.isEmpty()">
+            UPDATE kms_knowledge_article
+            SET is_public = #{isPublic}
+            WHERE category_id IN
+            <foreach collection="categoryIds" item="categoryId" open="(" separator="," close=")">
+                #{categoryId}
+            </foreach>
+        </if>
+    </update>
+
 </mapper>

+ 4 - 0
jeecg-module-kms/src/main/java/org/jeecg/modules/kms/knowledge/service/IArticleService.java

@@ -1,6 +1,8 @@
 package org.jeecg.modules.kms.knowledge.service;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
 import org.jeecg.modules.kms.knowledge.entity.Article;
 import org.jeecg.modules.kms.knowledge.param.FilterParam;
@@ -25,4 +27,6 @@ public interface IArticleService extends IService<Article> {
     void deleteBatchByIds(List<String> list);
 
     void restoreBatchByIds(List<String> list);
+
+    IPage<Article> queryListWithPermission(Page<Article> page, QueryWrapper<Article> queryWrapper);
 }

+ 7 - 0
jeecg-module-kms/src/main/java/org/jeecg/modules/kms/knowledge/service/impl/ArticleServiceImpl.java

@@ -163,4 +163,11 @@ public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> impl
             this.updateById(article);
         });
     }
+
+    @Override
+    public IPage<Article> queryListWithPermission(Page<Article> page, QueryWrapper<Article> queryWrapper) {
+        String sql = QueryGenerator.installAuthJdbc(Article.class);
+//        log.warn("查询权限SQL: " + queryWrapper.getCustomSqlSegment());
+        return this.baseMapper.queryListWithPermission(page, sql,queryWrapper);
+    }
 }

+ 14 - 1
jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/LoginController.java

@@ -9,7 +9,10 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.UsernamePasswordToken;
 import org.apache.shiro.authz.annotation.RequiresRoles;
+import org.apache.shiro.subject.Subject;
 import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.constant.CacheConstant;
 import org.jeecg.common.constant.CommonConstant;
@@ -77,6 +80,7 @@ public class LoginController {
 		Result<JSONObject> result = new Result<JSONObject>();
 		String username = sysLoginModel.getUsername();
 		String password = sysLoginModel.getPassword();
+		Boolean rememberMe = sysLoginModel.getRememberMe();
 		//update-begin-author:taoyan date:2022-11-7 for: issues/4109 平台用户登录失败锁定用户
 		if(isLoginFailOvertimes(username)){
 			return result.error500("该用户登录失败次数过多,请于10分钟后再次登录!");
@@ -131,7 +135,16 @@ public class LoginController {
 			result.error500("用户名或密码错误");
 			return result;
 		}
-				
+		// 使用 Shiro 进行登录并设置 "Remember Me"
+		UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);
+		try {
+			Subject subject = SecurityUtils.getSubject();
+			subject.login(token);
+			log.info("rememberMe"+rememberMe);
+		} catch (AuthenticationException e) {
+			result.error500("登录失败:" + e.getMessage());
+			return result;
+		}
 		//用户登录信息
 		userInfo(sysUser, result);
 //		//update-begin--Author:liusq  Date:20210126  for:登录成功,删除redis中的验证码

+ 24 - 13
jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/model/SysLoginModel.java

@@ -7,17 +7,20 @@ import io.swagger.annotations.ApiModelProperty;
  * 登录表单
  *
  * @Author scott
- * @since  2019-01-18
+ * @since 2019-01-18
  */
-@ApiModel(value="登录对象", description="登录对象")
+@ApiModel(value = "登录对象", description = "登录对象")
 public class SysLoginModel {
-	@ApiModelProperty(value = "账号")
+    @ApiModelProperty(value = "账号")
     private String username;
-	@ApiModelProperty(value = "密码")
+    @ApiModelProperty(value = "密码")
     private String password;
-	@ApiModelProperty(value = "验证码")
+
+    @ApiModelProperty(value = "记住我")
+    private Boolean rememberMe;
+    @ApiModelProperty(value = "验证码")
     private String captcha;
-	@ApiModelProperty(value = "验证码key")
+    @ApiModelProperty(value = "验证码key")
     private String checkKey;
 
     public String getUsername() {
@@ -44,12 +47,20 @@ public class SysLoginModel {
         this.captcha = captcha;
     }
 
-	public String getCheckKey() {
-		return checkKey;
-	}
+    public String getCheckKey() {
+        return checkKey;
+    }
+
+    public void setCheckKey(String checkKey) {
+        this.checkKey = checkKey;
+    }
+
+    public void setRememberMe(Boolean rememberMe) {
+        this.rememberMe = rememberMe;
+    }
+
+    public Boolean getRememberMe() {
+        return rememberMe;
+    }
 
-	public void setCheckKey(String checkKey) {
-		this.checkKey = checkKey;
-	}
-    
 }