diff --git a/src/main/java/com/tale/bootstrap/Bootstrap.java b/src/main/java/com/tale/bootstrap/Bootstrap.java index 0f356b85..9f19def2 100644 --- a/src/main/java/com/tale/bootstrap/Bootstrap.java +++ b/src/main/java/com/tale/bootstrap/Bootstrap.java @@ -2,10 +2,10 @@ import com.blade.Blade; import com.blade.Environment; -import com.blade.event.BeanProcessor; import com.blade.ioc.Ioc; import com.blade.ioc.annotation.Bean; import com.blade.ioc.annotation.Inject; +import com.blade.kit.JsonKit; import com.blade.kit.StringKit; import com.blade.loader.BladeLoader; import com.blade.mvc.view.template.JetbrickTemplateEngine; @@ -15,6 +15,7 @@ import com.tale.extension.Commons; import com.tale.extension.JetTag; import com.tale.extension.Theme; +import com.tale.model.dto.RememberMe; import com.tale.model.dto.Types; import com.tale.service.OptionsService; import com.tale.service.SiteService; @@ -30,6 +31,7 @@ import java.util.List; import static com.tale.bootstrap.TaleConst.CLASSPATH; +import static com.tale.bootstrap.TaleConst.OPTION_SAFE_REMEMBER_ME; /** * Tale初始化进程 @@ -72,7 +74,7 @@ public void load(Blade blade) { // 扫描主题下面的所有自定义宏 String themeDir = CLASSPATH + "templates" + File.separatorChar + "themes"; File[] dir = new File(themeDir).listFiles(); - if(null != dir){ + if (null != dir) { for (File f : dir) { if (f.isDirectory() && Files.exists(Paths.get(f.getPath() + File.separatorChar + "macros.html"))) { String macroName = File.separatorChar + "themes" + File.separatorChar + f.getName() + File.separatorChar + "macros.html"; @@ -100,7 +102,6 @@ public void load(Blade blade) { TaleConst.ENABLED_CDN = environment.getBoolean("app.enableCdn", false); TaleConst.MAX_FILE_SIZE = environment.getInt("app.max-file-size", 20480); - TaleConst.AES_SALT = environment.get("app.salt", "012c456789abcdef"); TaleConst.OPTIONS.addAll(optionsService.getOptions()); String ips = TaleConst.OPTIONS.get(Types.BLOCK_IPS, ""); if (StringKit.isNotBlank(ips)) { @@ -110,6 +111,12 @@ public void load(Blade blade) { TaleConst.INSTALLED = Boolean.TRUE; } + String rememberToken = optionsService.getOption(OPTION_SAFE_REMEMBER_ME); + if (StringKit.isNotEmpty(rememberToken)) { + RememberMe rememberMe = JsonKit.formJson(rememberToken, RememberMe.class); + TaleConst.REMEMBER_TOKEN = rememberMe.getToken(); + } + BaseController.THEME = "themes/" + Commons.site_option("site_theme"); TaleConst.BCONF = environment; diff --git a/src/main/java/com/tale/bootstrap/TaleConst.java b/src/main/java/com/tale/bootstrap/TaleConst.java index 60db2924..317d5572 100644 --- a/src/main/java/com/tale/bootstrap/TaleConst.java +++ b/src/main/java/com/tale/bootstrap/TaleConst.java @@ -17,13 +17,14 @@ public class TaleConst { public static final String CLASSPATH = new File(AdminApiController.class.getResource("/").getPath()).getPath() + File.separatorChar; - public static final String USER_IN_COOKIE = "S_L_ID"; - public static String AES_SALT = "0123456789abcdef"; - public static String LOGIN_SESSION_KEY = "login_user"; - public static Environment OPTIONS = Environment.of(new HashMap<>()); - public static Boolean INSTALLED = false; - public static Boolean ENABLED_CDN = true; - public static Environment BCONF = null; + public static final String REMEMBER_IN_COOKIE = "remember_me"; + public static final String LOGIN_ERROR_COUNT = "login_error_count"; + public static String LOGIN_SESSION_KEY = "login_user"; + public static String REMEMBER_TOKEN = ""; + public static Environment OPTIONS = Environment.of(new HashMap<>()); + public static Boolean INSTALLED = false; + public static Boolean ENABLED_CDN = true; + public static Environment BCONF = null; /** * 最大页码 @@ -114,4 +115,6 @@ public class TaleConst { public static final String OPTION_ALLOW_INSTALL = "allow_install"; public static final String OPTION_ALLOW_COMMENT_AUDIT = "allow_comment_audit"; public static final String OPTION_ALLOW_CLOUD_CDN = "allow_cloud_CDN"; + public static final String OPTION_SAFE_REMEMBER_ME = "safe_remember_me"; + } \ No newline at end of file diff --git a/src/main/java/com/tale/controller/IndexController.java b/src/main/java/com/tale/controller/IndexController.java index d9af0595..e16ae318 100644 --- a/src/main/java/com/tale/controller/IndexController.java +++ b/src/main/java/com/tale/controller/IndexController.java @@ -1,174 +1,172 @@ -package com.tale.controller; - -import com.blade.ioc.annotation.Inject; -import com.blade.mvc.annotation.*; -import com.blade.mvc.http.Request; -import com.blade.mvc.http.Response; -import com.blade.mvc.http.Session; -import com.tale.bootstrap.TaleConst; -import com.tale.model.dto.Archive; -import com.tale.model.dto.Types; -import com.tale.model.entity.Contents; -import com.tale.model.params.PageParam; -import com.tale.service.SiteService; -import com.tale.utils.TaleUtils; -import io.github.biezhi.anima.enums.OrderBy; -import io.github.biezhi.anima.page.Page; -import lombok.extern.slf4j.Slf4j; - -import java.util.List; - -import static io.github.biezhi.anima.Anima.select; - -/** - * 首页、归档、Feed、评论 - * - * @author biezhi - * @since 1.3.1 - */ -@Path -@Slf4j -public class IndexController extends BaseController { - - @Inject - private SiteService siteService; - - /** - * 首页 - * - * @return - */ - @GetRoute - public String index(Request request, PageParam pageParam) { - return this.index(request, 1, pageParam.getLimit()); - } - - /** - * 首页分页 - * - * @param request - * @param page - * @param limit - * @return - */ - @GetRoute(value = {"page/:page", "page/:page.html"}) - public String index(Request request, @PathParam int page, @Param(defaultValue = "12") int limit) { - page = page < 0 || page > TaleConst.MAX_PAGE ? 1 : page; - if (page > 1) { - this.title(request, "第" + page + "页"); - } - request.attribute("page_num", page); - request.attribute("limit", limit); - request.attribute("is_home", true); - request.attribute("page_prefix", "/page"); - return this.render("index"); - } - - - /** - * 搜索页 - * - * @param keyword - * @return - */ - @GetRoute(value = {"search/:keyword", "search/:keyword.html"}) - public String search(Request request, @PathParam String keyword, @Param(defaultValue = "12") int limit) { - return this.search(request, keyword, 1, limit); - } - - @GetRoute(value = {"search", "search.html"}) - public String search(Request request, @Param(defaultValue = "12") int limit) { - String keyword = request.query("s").orElse(""); - return this.search(request, keyword, 1, limit); - } - - @GetRoute(value = {"search/:keyword/:page", "search/:keyword/:page.html"}) - public String search(Request request, @PathParam String keyword, @PathParam int page, @Param(defaultValue = "12") int limit) { - - page = page < 0 || page > TaleConst.MAX_PAGE ? 1 : page; - - Page articles = select().from(Contents.class) - .where(Contents::getType, Types.ARTICLE) - .and(Contents::getStatus, Types.PUBLISH) - .like(Contents::getTitle, "%" + keyword + "%") - .order(Contents::getCreated, OrderBy.DESC) - .page(page, limit); - - request.attribute("articles", articles); - request.attribute("type", "搜索"); - request.attribute("keyword", keyword); - request.attribute("page_prefix", "/search/" + keyword); - return this.render("page-category"); - } - - /** - * 归档页 - * - * @return - */ - @GetRoute(value = {"archives", "archives.html"}) - public String archives(Request request) { - List archives = siteService.getArchives(); - request.attribute("archives", archives); - request.attribute("is_archive", true); - return this.render("archives"); - } - - /** - * feed页 - * - * @return - */ - @GetRoute(value = {"feed", "feed.xml", "atom.xml"}) - public void feed(Response response) { - - List articles = select().from(Contents.class) - .where(Contents::getType, Types.ARTICLE) - .and(Contents::getStatus, Types.PUBLISH) - .and(Contents::getAllowFeed, true) - .order(Contents::getCreated, OrderBy.DESC) - .all(); - - try { - String xml = TaleUtils.getRssXml(articles); - response.contentType("text/xml; charset=utf-8"); - response.body(xml); - } catch (Exception e) { - log.error("生成 rss 失败", e); - } - } - - /** - * sitemap 站点地图 - * - * @return - */ - @GetRoute(value = {"sitemap", "sitemap.xml"}) - public void sitemap(Response response) { - List articles = select().from(Contents.class) - .where(Contents::getType, Types.ARTICLE) - .and(Contents::getStatus, Types.PUBLISH) - .and(Contents::getAllowFeed, true) - .order(Contents::getCreated, OrderBy.DESC) - .all(); - try { - String xml = TaleUtils.getSitemapXml(articles); - response.contentType("text/xml; charset=utf-8"); - response.body(xml); - } catch (Exception e) { - log.error("生成 sitemap 失败", e); - } - } - - /** - * 注销 - * - * @param session - * @param response - */ - @Route(value = "logout") - public void logout(Session session, Response response) { - TaleUtils.logout(session, response); - } - +package com.tale.controller; + +import com.blade.ioc.annotation.Inject; +import com.blade.mvc.RouteContext; +import com.blade.mvc.annotation.*; +import com.blade.mvc.http.Request; +import com.blade.mvc.http.Response; +import com.blade.mvc.http.Session; +import com.tale.bootstrap.TaleConst; +import com.tale.model.dto.Archive; +import com.tale.model.dto.Types; +import com.tale.model.entity.Contents; +import com.tale.model.params.PageParam; +import com.tale.service.SiteService; +import com.tale.utils.TaleUtils; +import io.github.biezhi.anima.enums.OrderBy; +import io.github.biezhi.anima.page.Page; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +import static io.github.biezhi.anima.Anima.select; + +/** + * 首页、归档、Feed、评论 + * + * @author biezhi + * @since 1.3.1 + */ +@Path +@Slf4j +public class IndexController extends BaseController { + + @Inject + private SiteService siteService; + + /** + * 首页 + * + * @return + */ + @GetRoute + public String index(Request request, PageParam pageParam) { + return this.index(request, 1, pageParam.getLimit()); + } + + /** + * 首页分页 + * + * @param request + * @param page + * @param limit + * @return + */ + @GetRoute(value = {"page/:page", "page/:page.html"}) + public String index(Request request, @PathParam int page, @Param(defaultValue = "12") int limit) { + page = page < 0 || page > TaleConst.MAX_PAGE ? 1 : page; + if (page > 1) { + this.title(request, "第" + page + "页"); + } + request.attribute("page_num", page); + request.attribute("limit", limit); + request.attribute("is_home", true); + request.attribute("page_prefix", "/page"); + return this.render("index"); + } + + + /** + * 搜索页 + * + * @param keyword + * @return + */ + @GetRoute(value = {"search/:keyword", "search/:keyword.html"}) + public String search(Request request, @PathParam String keyword, @Param(defaultValue = "12") int limit) { + return this.search(request, keyword, 1, limit); + } + + @GetRoute(value = {"search", "search.html"}) + public String search(Request request, @Param(defaultValue = "12") int limit) { + String keyword = request.query("s").orElse(""); + return this.search(request, keyword, 1, limit); + } + + @GetRoute(value = {"search/:keyword/:page", "search/:keyword/:page.html"}) + public String search(Request request, @PathParam String keyword, @PathParam int page, @Param(defaultValue = "12") int limit) { + + page = page < 0 || page > TaleConst.MAX_PAGE ? 1 : page; + + Page articles = select().from(Contents.class) + .where(Contents::getType, Types.ARTICLE) + .and(Contents::getStatus, Types.PUBLISH) + .like(Contents::getTitle, "%" + keyword + "%") + .order(Contents::getCreated, OrderBy.DESC) + .page(page, limit); + + request.attribute("articles", articles); + request.attribute("type", "搜索"); + request.attribute("keyword", keyword); + request.attribute("page_prefix", "/search/" + keyword); + return this.render("page-category"); + } + + /** + * 归档页 + * + * @return + */ + @GetRoute(value = {"archives", "archives.html"}) + public String archives(Request request) { + List archives = siteService.getArchives(); + request.attribute("archives", archives); + request.attribute("is_archive", true); + return this.render("archives"); + } + + /** + * feed页 + * + * @return + */ + @GetRoute(value = {"feed", "feed.xml", "atom.xml"}) + public void feed(Response response) { + + List articles = select().from(Contents.class) + .where(Contents::getType, Types.ARTICLE) + .and(Contents::getStatus, Types.PUBLISH) + .and(Contents::getAllowFeed, true) + .order(Contents::getCreated, OrderBy.DESC) + .all(); + + try { + String xml = TaleUtils.getRssXml(articles); + response.contentType("text/xml; charset=utf-8"); + response.body(xml); + } catch (Exception e) { + log.error("生成 rss 失败", e); + } + } + + /** + * sitemap 站点地图 + * + * @return + */ + @GetRoute(value = {"sitemap", "sitemap.xml"}) + public void sitemap(Response response) { + List articles = select().from(Contents.class) + .where(Contents::getType, Types.ARTICLE) + .and(Contents::getStatus, Types.PUBLISH) + .and(Contents::getAllowFeed, true) + .order(Contents::getCreated, OrderBy.DESC) + .all(); + try { + String xml = TaleUtils.getSitemapXml(articles); + response.contentType("text/xml; charset=utf-8"); + response.body(xml); + } catch (Exception e) { + log.error("生成 sitemap 失败", e); + } + } + + /** + * 注销 + */ + @Route(value = "logout") + public void logout(RouteContext context) { + TaleUtils.logout(context); + } + } \ No newline at end of file diff --git a/src/main/java/com/tale/controller/admin/AuthController.java b/src/main/java/com/tale/controller/admin/AuthController.java index 91cfd1eb..cbf9fb8a 100644 --- a/src/main/java/com/tale/controller/admin/AuthController.java +++ b/src/main/java/com/tale/controller/admin/AuthController.java @@ -4,11 +4,9 @@ import com.blade.kit.DateKit; import com.blade.kit.EncryptKit; import com.blade.kit.StringKit; +import com.blade.mvc.RouteContext; import com.blade.mvc.annotation.Path; import com.blade.mvc.annotation.PostRoute; -import com.blade.mvc.http.Request; -import com.blade.mvc.http.Response; -import com.blade.mvc.http.Session; import com.blade.mvc.ui.RestResponse; import com.tale.annotation.SysLog; import com.tale.bootstrap.TaleConst; @@ -19,10 +17,12 @@ import com.tale.validators.CommonValidator; import lombok.extern.slf4j.Slf4j; +import static com.tale.bootstrap.TaleConst.LOGIN_ERROR_COUNT; import static io.github.biezhi.anima.Anima.select; /** * 登录,退出 + *

* Created by biezhi on 2017/2/21. */ @Slf4j @@ -31,20 +31,20 @@ public class AuthController extends BaseController { @SysLog("登录后台") @PostRoute("login") - public RestResponse doLogin(LoginParam loginParam, Request request, - Session session, Response response) { + public RestResponse doLogin(LoginParam loginParam, RouteContext context) { CommonValidator.valid(loginParam); - Integer error_count = cache.get("login_error_count"); + Integer errorCount = cache.get(LOGIN_ERROR_COUNT); try { - error_count = null == error_count ? 0 : error_count; - if (error_count > 3) { + errorCount = null == errorCount ? 0 : errorCount; + if (errorCount > 3) { return RestResponse.fail("您输入密码已经错误超过3次,请10分钟后尝试"); } long count = new Users().where("username", loginParam.getUsername()).count(); if (count < 1) { + errorCount += 1; return RestResponse.fail("不存在该用户"); } String pwd = EncryptKit.md5(loginParam.getUsername(), loginParam.getPassword()); @@ -54,21 +54,27 @@ public RestResponse doLogin(LoginParam loginParam, Request request, .and(Users::getPassword, pwd).one(); if (null == user) { + errorCount += 1; return RestResponse.fail("用户名或密码错误"); } - session.attribute(TaleConst.LOGIN_SESSION_KEY, user); + context.session().attribute(TaleConst.LOGIN_SESSION_KEY, user); + if (StringKit.isNotBlank(loginParam.getRememberMe())) { - TaleUtils.setCookie(response, user.getUid()); + TaleUtils.setCookie(context, user.getUid()); } Users temp = new Users(); temp.setLogged(DateKit.nowUnix()); temp.updateById(user.getUid()); log.info("登录成功:{}", loginParam.getUsername()); - cache.set("login_error_count", 0); + + cache.set(LOGIN_ERROR_COUNT, 0); + + return RestResponse.ok(); + } catch (Exception e) { - error_count += 1; - cache.set("login_error_count", error_count, 10 * 60); + errorCount += 1; + cache.set(LOGIN_ERROR_COUNT, errorCount, 10 * 60); String msg = "登录失败"; if (e instanceof ValidatorException) { msg = e.getMessage(); @@ -77,7 +83,6 @@ public RestResponse doLogin(LoginParam loginParam, Request request, } return RestResponse.fail(msg); } - return RestResponse.ok(); } } diff --git a/src/main/java/com/tale/controller/admin/SystemController.java b/src/main/java/com/tale/controller/admin/SystemController.java index 96a3e92e..3b063033 100644 --- a/src/main/java/com/tale/controller/admin/SystemController.java +++ b/src/main/java/com/tale/controller/admin/SystemController.java @@ -6,9 +6,9 @@ import com.blade.mvc.annotation.Param; import com.blade.mvc.annotation.Path; import com.blade.mvc.annotation.PostRoute; -import com.blade.mvc.http.Request; import com.blade.mvc.ui.RestResponse; import com.tale.annotation.SysLog; +import com.tale.bootstrap.TaleConst; import com.tale.controller.BaseController; import com.tale.model.entity.Users; import com.tale.service.OptionsService; @@ -31,7 +31,7 @@ public class SystemController extends BaseController { @SysLog("保存个人信息") @PostRoute("profile") - public RestResponse saveProfile(@Param String screenName, @Param String email, Request request) { + public RestResponse saveProfile(@Param String screenName, @Param String email) { Users users = this.user(); if (StringKit.isNotBlank(screenName) && StringKit.isNotBlank(email)) { Users temp = new Users(); @@ -44,7 +44,7 @@ public RestResponse saveProfile(@Param String screenName, @Param String email, R @SysLog("修改登录密码") @PostRoute("password") - public RestResponse upPwd(@Param String old_password, @Param String password, Request request) { + public RestResponse upPwd(@Param String old_password, @Param String password) { Users users = this.user(); if (StringKit.isBlank(old_password) || StringKit.isBlank(password)) { return RestResponse.fail("请确认信息输入完整"); @@ -61,6 +61,7 @@ public RestResponse upPwd(@Param String old_password, @Param String password, Re String pwd = EncryptKit.md5(users.getUsername() + password); temp.setPassword(pwd); temp.updateById(users.getUid()); + optionsService.deleteOption(TaleConst.OPTION_SAFE_REMEMBER_ME); return RestResponse.ok(); } diff --git a/src/main/java/com/tale/extension/Theme.java b/src/main/java/com/tale/extension/Theme.java index 426ab6c7..9aac7623 100644 --- a/src/main/java/com/tale/extension/Theme.java +++ b/src/main/java/com/tale/extension/Theme.java @@ -647,6 +647,19 @@ public static Page comments(int limit) { return comments; } + /** + * 获取当前文章/页面的评论数量 + * + * @return 当前页面的评论数量 + */ + public static long commentsCount() { + Contents contents = current_article(); + if (null == contents) { + return 0; + } + return siteService.getCommentCount(contents.getCid()); + } + /** * 分页 * diff --git a/src/main/java/com/tale/hooks/BaseWebHook.java b/src/main/java/com/tale/hooks/BaseWebHook.java index c724fb60..6259fd88 100644 --- a/src/main/java/com/tale/hooks/BaseWebHook.java +++ b/src/main/java/com/tale/hooks/BaseWebHook.java @@ -21,31 +21,28 @@ public class BaseWebHook implements WebHook { @Override public boolean before(RouteContext context) { - Request request = context.request(); - Response response = context.response(); - - String uri = request.uri(); - String ip = request.address(); + String uri = context.uri(); + String ip = context.address(); // 禁止该ip访问 if (TaleConst.BLOCK_IPS.contains(ip)) { - response.text("You have been banned, brother"); + context.text("You have been banned, brother"); return false; } - log.info("IP: {}, UserAgent: {}", ip, request.userAgent()); + log.info("IP: {}, UserAgent: {}", ip, context.userAgent()); if (uri.startsWith(TaleConst.STATIC_URI)) { return true; } if (!TaleConst.INSTALLED && !uri.startsWith(TaleConst.INSTALL_URI)) { - response.redirect(TaleConst.INSTALL_URI); + context.redirect(TaleConst.INSTALL_URI); return false; } if (TaleConst.INSTALLED) { - return isRedirect(request, response); + return isRedirect(context); } return true; } @@ -69,22 +66,24 @@ public boolean after(RouteContext context) { return true; } - private boolean isRedirect(Request request, Response response) { + private boolean isRedirect(RouteContext context) { Users user = TaleUtils.getLoginUser(); - String uri = request.uri(); - if (null == user) { - Integer uid = TaleUtils.getCookieUid(request); + String uri = context.uri(); + if (uri.startsWith(TaleConst.ADMIN_URI) && !uri.startsWith(TaleConst.LOGIN_URI)) { + context.attribute(TaleConst.PLUGINS_MENU_NAME, TaleConst.PLUGIN_MENUS); + if(null != user){ + return true; + } + + Integer uid = TaleUtils.getCookieUid(context); if (null != uid) { user = select().from(Users.class).byId(uid); - request.session().attribute(TaleConst.LOGIN_SESSION_KEY, user); + context.session().attribute(TaleConst.LOGIN_SESSION_KEY, user); } - } - if (uri.startsWith(TaleConst.ADMIN_URI) && !uri.startsWith(TaleConst.LOGIN_URI)) { if (null == user) { - response.redirect(TaleConst.LOGIN_URI); + context.redirect(TaleConst.LOGIN_URI); return false; } - request.attribute(TaleConst.PLUGINS_MENU_NAME, TaleConst.PLUGIN_MENUS); } return true; } diff --git a/src/main/java/com/tale/model/dto/RememberMe.java b/src/main/java/com/tale/model/dto/RememberMe.java new file mode 100644 index 00000000..157bdefb --- /dev/null +++ b/src/main/java/com/tale/model/dto/RememberMe.java @@ -0,0 +1,21 @@ +package com.tale.model.dto; + +import lombok.Data; + +import java.util.List; + +/** + * 记住我数据结构 + * + * @author biezhi + * @date 2018-10-25 + */ +@Data +public class RememberMe { + + private Integer uid; + private Integer expires; + private String token; + private List recentIp; + +} diff --git a/src/main/java/com/tale/service/CommentsService.java b/src/main/java/com/tale/service/CommentsService.java index 0eb63026..efe969dc 100644 --- a/src/main/java/com/tale/service/CommentsService.java +++ b/src/main/java/com/tale/service/CommentsService.java @@ -99,6 +99,18 @@ public Page getComments(Integer cid, int page, int limit) { return commentsPage.map(this::apply); } + /** + * 获取文章下的评论统计 + * + * @param cid 文章ID + */ + public long getCommentCount(Integer cid) { + if (null == cid) { + return 0; + } + return select().from(Comments.class).where(Comments::getCid, cid).count(); + } + /** * 获取该评论下的追加评论 * diff --git a/src/main/java/com/tale/service/SiteService.java b/src/main/java/com/tale/service/SiteService.java index 57ebac76..f98f1a0f 100644 --- a/src/main/java/com/tale/service/SiteService.java +++ b/src/main/java/com/tale/service/SiteService.java @@ -315,6 +315,15 @@ public Page getComments(Integer cid, int page, int limit) { return commentsService.getComments(cid, page, limit); } + /** + * 获取文章的评论总数 + * + * @param cid 文章id + */ + public long getCommentCount(Integer cid) { + return commentsService.getCommentCount(cid); + } + /** * 清楚缓存 * diff --git a/src/main/java/com/tale/utils/TaleUtils.java b/src/main/java/com/tale/utils/TaleUtils.java index 4dd99518..01f5b5a2 100644 --- a/src/main/java/com/tale/utils/TaleUtils.java +++ b/src/main/java/com/tale/utils/TaleUtils.java @@ -1,10 +1,9 @@ package com.tale.utils; -import com.blade.kit.DateKit; -import com.blade.kit.Hashids; -import com.blade.kit.StringKit; -import com.blade.mvc.http.Request; -import com.blade.mvc.http.Response; +import com.blade.kit.UUID; +import com.blade.kit.*; +import com.blade.mvc.RouteContext; +import com.blade.mvc.http.Cookie; import com.blade.mvc.http.Session; import com.sun.syndication.feed.rss.Channel; import com.sun.syndication.feed.rss.Content; @@ -14,7 +13,9 @@ import com.tale.bootstrap.TaleConst; import com.tale.extension.Commons; import com.tale.extension.Theme; +import com.tale.model.dto.RememberMe; import com.tale.model.entity.Contents; +import com.tale.model.entity.Options; import com.tale.model.entity.Users; import org.commonmark.Extension; import org.commonmark.ext.gfm.tables.TablesExtension; @@ -29,13 +30,16 @@ import java.io.File; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.*; import java.util.List; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import static com.tale.bootstrap.TaleConst.*; +import static io.github.biezhi.anima.Anima.select; +import static io.github.biezhi.anima.Anima.update; /** * Tale工具类 @@ -45,12 +49,9 @@ public class TaleUtils { /** - * 一个月 + * 一周 */ - private static final int ONE_MONTH = 30 * 24 * 60 * 60; - private static final Random R = new Random(); - private static final Hashids HASH_IDS = new Hashids(TaleConst.AES_SALT); - private static final long[] HASH_PREFIX = {-1, 2, 0, 1, 7, 0, 9}; + private static final int ONE_MONTH = 7 * 24 * 60 * 60; /** * 匹配邮箱正则 @@ -61,28 +62,66 @@ public class TaleUtils { private static final Pattern SLUG_REGEX = Pattern.compile("^[A-Za-z0-9_-]{3,50}$", Pattern.CASE_INSENSITIVE); /** - * 设置记住密码cookie - * - * @param response - * @param uid + * 设置记住密码 cookie */ - public static void setCookie(Response response, Integer uid) { - try { - HASH_PREFIX[0] = uid; - String val = HASH_IDS.encode(HASH_PREFIX); - HASH_PREFIX[0] = -1; -// String val = new String(EncrypKit.encryptAES(uid.toString().getBytes(), TaleConst.AES_SALT.getBytes())); - boolean isSSL = Commons.site_url().startsWith("https"); - response.cookie("/", TaleConst.USER_IN_COOKIE, val, ONE_MONTH, isSSL); - } catch (Exception e) { - e.printStackTrace(); + public static void setCookie(RouteContext context, Integer uid) { + boolean isSSL = Commons.site_url().startsWith("https"); + + String token = EncryptKit.md5(UUID.UU64()); + RememberMe rememberMe = new RememberMe(); + rememberMe.setUid(uid); + rememberMe.setExpires(DateKit.nowUnix() + ONE_MONTH); + rememberMe.setRecentIp(Collections.singletonList(context.address())); + rememberMe.setToken(token); + + long count = select().from(Options.class).where(Options::getName, OPTION_SAFE_REMEMBER_ME).count(); + if (count == 0) { + Options options = new Options(); + options.setName(OPTION_SAFE_REMEMBER_ME); + options.setValue(JsonKit.toString(rememberMe)); + options.setDescription("记住我 Token"); + options.save(); + } else { + update().from(Options.class).set(Options::getValue, JsonKit.toString(rememberMe)) + .where(Options::getName, OPTION_SAFE_REMEMBER_ME) + .execute(); + } + + Cookie cookie = new Cookie(); + cookie.name(REMEMBER_IN_COOKIE); + cookie.value(token); + cookie.httpOnly(true); + cookie.secure(isSSL); + cookie.maxAge(ONE_MONTH); + cookie.path("/"); + + context.response().cookie(cookie); + } + + public static Integer getCookieUid(RouteContext context) { + String rememberToken = context.cookie(REMEMBER_IN_COOKIE); + if (null == rememberToken || rememberToken.isEmpty() || REMEMBER_TOKEN.isEmpty()) { + return null; + } + if (!REMEMBER_TOKEN.equals(rememberToken)) { + return null; + } + Options options = select().from(Options.class).where(Options::getName, OPTION_SAFE_REMEMBER_ME).one(); + if (null == options) { + return null; + } + RememberMe rememberMe = JsonKit.formJson(options.getValue(), RememberMe.class); + if (rememberMe.getExpires() < DateKit.nowUnix()) { + return null; } + if (!rememberMe.getRecentIp().contains(context.address())) { + return null; + } + return rememberMe.getUid(); } /** * 返回当前登录用户 - * - * @return */ public static Users getLoginUser() { Session session = com.blade.mvc.WebContext.request().session(); @@ -95,68 +134,23 @@ public static Users getLoginUser() { /** * 退出登录状态 - * - * @param session - * @param response - */ - public static void logout(Session session, Response response) { - session.removeAttribute(TaleConst.LOGIN_SESSION_KEY); - response.removeCookie(TaleConst.USER_IN_COOKIE); - response.redirect(Commons.site_url()); - } - - /** - * 获取cookie中的用户id - * - * @param request - * @return */ - public static Integer getCookieUid(Request request) { - if (null != request) { - String value = request.cookie(TaleConst.USER_IN_COOKIE); - if (StringKit.isNotBlank(value)) { - try { - long[] ids = HASH_IDS.decode(value); - if (null != ids && ids.length > 0) { - return Long.valueOf(ids[0]).intValue(); - } - } catch (Exception e) { - } - } - } - return null; - } - - /** - * 重新拼接字符串 - * - * @param arr - * @return - */ - public static String rejoin(String[] arr) { - if (null == arr) { - return ""; - } - if (arr.length == 1) { - return "'" + arr[0] + "'"; - } - String a = String.join("','", arr); - a = a.substring(2) + "'"; - return a; + public static void logout(RouteContext context) { + TaleConst.REMEMBER_TOKEN = ""; + context.session().remove(TaleConst.LOGIN_SESSION_KEY); + context.response().removeCookie(TaleConst.REMEMBER_IN_COOKIE); + context.redirect(Commons.site_url()); } /** * markdown转换为html - * - * @param markdown - * @return */ public static String mdToHtml(String markdown) { if (StringKit.isBlank(markdown)) { return ""; } - List extensions = Arrays.asList(TablesExtension.create()); + List extensions = Collections.singletonList(TablesExtension.create()); Parser parser = Parser.builder().extensions(extensions).build(); Node document = parser.parse(markdown); HtmlRenderer renderer = HtmlRenderer.builder() @@ -183,14 +177,14 @@ public void setAttributes(Node node, String tagName, Map attribu if (node instanceof Link) { attributes.put("target", "_blank"); } + if (node instanceof org.commonmark.node.Image) { + attributes.put("title", attributes.get("alt")); + } } } /** * 提取html中的文字 - * - * @param html - * @return */ public static String htmlToText(String html) { if (StringKit.isNotBlank(html)) { @@ -201,9 +195,6 @@ public static String htmlToText(String html) { /** * 判断文件是否是图片类型 - * - * @param imageFile - * @return */ public static boolean isImage(File imageFile) { if (!imageFile.exists()) { @@ -222,9 +213,6 @@ public static boolean isImage(File imageFile) { /** * 判断是否是邮箱 - * - * @param emailStr - * @return */ public static boolean isEmail(String emailStr) { Matcher matcher = VALID_EMAIL_ADDRESS_REGEX.matcher(emailStr); @@ -233,9 +221,6 @@ public static boolean isEmail(String emailStr) { /** * 判断是否是合法路径 - * - * @param slug - * @return */ public static boolean isPath(String slug) { if (StringKit.isNotBlank(slug)) { @@ -250,10 +235,6 @@ public static boolean isPath(String slug) { /** * 获取RSS输出 - * - * @param articles - * @return - * @throws FeedException */ public static String getRssXml(java.util.List articles) throws FeedException { Channel channel = new Channel("rss_2.0"); @@ -331,9 +312,6 @@ private static Url parse(Contents contents) { /** * 替换HTML脚本 - * - * @param value - * @return */ public static String cleanXSS(String value) { //You'll need to remove the spaces from the html entities below @@ -361,8 +339,8 @@ public static int[] random(int max, int len) { } //随机交换values.length次 for (int i = 0; i < values.length; i++) { - temp1 = Math.abs(R.nextInt()) % (values.length - 1); //随机产生一个位置 - temp2 = Math.abs(R.nextInt()) % (values.length - 1); //随机产生另一个位置 + temp1 = Math.abs(ThreadLocalRandom.current().nextInt()) % (values.length - 1); //随机产生一个位置 + temp2 = Math.abs(ThreadLocalRandom.current().nextInt()) % (values.length - 1); //随机产生另一个位置 if (temp1 != temp2) { temp3 = values[temp1]; values[temp1] = values[temp2]; @@ -398,7 +376,7 @@ public static String getFileKey(String name) { } public static String getFileName(String path) { - File tempFile = new File(path.trim()); + File tempFile = new File(path.trim()); String fileName = tempFile.getName(); return fileName; diff --git a/src/main/resources/templates/admin/themes.html b/src/main/resources/templates/admin/themes.html index 4ad0a766..0a1122db 100644 --- a/src/main/resources/templates/admin/themes.html +++ b/src/main/resources/templates/admin/themes.html @@ -8,7 +8,7 @@

主题管理

- +
主题:{{item.name}} (当前主题)