111.【金橘社区1.0】
金橘社区1.0版本
- 1. template包中创建包静态资源路径一样.
- 2. SpringBoot自定义400、500错误。只需要在template包下创建error包即可。
- 3. 加入我们要使用mybatis-plus那么条件构造其实际上就是对SQL的拼接操作。构造器的泛型是: 实体类
- 4.我们在验证登入操作的时候,只需要根据username进行判断即可,因为这样可以减少对数据库的一次访问
- 1. 渐变动态背景CSS .
- 2. sleep() 休眠的工具类
- 3. 利用JS页面的跳转,我们一定要用异步跳转
- 4. 好看的动态背景推荐
- 6. 炫酷的搜索框
- 7. 返回顶部
- 8. 特效烟花点击
- 9. 前端引入 markdown编辑器 ---- 一定要和html处于同级目录下.
- 10. 百度搜索框
- 11. 二维码弹窗!!!!!
- 12. 文本框不能为空格的属性,一个属性就可以
(一)、SpringBoot整合SpringSecurity
金橘社区官网: http://www.jsxs1.cn/
Gitee仓库地址: https://gitee.com/lwt121788/ckqn
1.导入依赖
我们使用Mybatis-plu自动生成代码: 在Mapper层没有注解。所以我们需要自己加
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.7</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.jsxs</groupId> <artifactId>Kumquat</artifactId> <version>0.0.1-SNAPSHOT</version> <name>Kumquat</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!--web 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--测试启动类 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- JDBC驱动器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--mysql连接驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--mybatis-plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency> <!--自动生成代码依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.3.2</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-annotations</artifactId> <version>1.5.22</version> </dependency> <!-- thymeleaf --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> <version>2.7.7</version> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- JSON--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.26</version> </dependency> <!--SpringSecurity --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 --> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity5</artifactId> <version>3.0.4.RELEASE</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> <skip>true</skip> </configuration> </plugin> </plugins> </build> </project>
2.数据库
CREATE DATABASE CQAN; USE CQAN; # 用户表 CREATE TABLE `ckqn_user` ( `id` int(10) NOT NULL AUTO_INCREMENT COMMENT '自增id', `uid` varchar(200) NOT NULL COMMENT '用户编号', `role_id` int(10) NOT NULL COMMENT '角色编号', `username` varchar(100) NOT NULL COMMENT '用户名', `password` varchar(200) NOT NULL COMMENT '密码', `avatar` varchar(500) NOT NULL DEFAULT '/images/avatar/avatar-1.jpg' COMMENT '头像', `login_date` datetime NOT NULL COMMENT '登录时间', `gmt_create` datetime NOT NULL COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=840 DEFAULT CHARSET=utf8; # 用户角色 CREATE TABLE `ckqn_user_role` ( `id` int(10) NOT NULL AUTO_INCREMENT COMMENT '角色编号', `name` varchar(200) NOT NULL COMMENT '角色名称', `description` varchar(500) NOT NULL DEFAULT '无描述...' COMMENT '角色描述', `gmt_create` datetime NOT NULL COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
3.登入表单
<form th:action="@{/code}" method="post"> <div class="inputBox"> <input type="text" placeholder="账户ID" name="username" id="name" required onblur="a1()" onkeyup="this.value=this.value.replace(/\s+/g,'')"> <i id="username" style="z-index: 9999"></i> </div> <div class="inputBox"> <input type="password" placeholder="密码" name="password" required onkeyup="this.value=this.value.replace(/\s+/g,'')"> </div> <div class="inputBox"> <input type="submit" value="登录"> </div> <p class="forget">忘记密码?<a href="#"> 点击这里 </a></p> <p class="forget">没有账户?<a href="#"> 注册 </a></p> </form>
4. 添加配置类 SecurityConfig
package com.jsxs.kumquat.config; import com.jsxs.kumquat.service.impl.CkqnUserServiceImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; import javax.annotation.Resource; /** * @Author Jsxs * @Date 2023/4/10 20:56 * @PackageName:com.jsxs.kumquat.config * @ClassName: SecurityConfig * @Description: TODO 授权文件 * @Version 1.0 */ @EnableWebSecurity @Configuration @Component public class SecurityConfig extends WebSecurityConfigurerAdapter { // 调用的是业务实现类 @Resource private CkqnUserServiceImpl ckqnUserService; // 密码加密方式 @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } /** * 认证 * * @param http * @throws Exception permitAll -> 无条件访问 * authenticated -> 需要认证才能访问, /* 除了无条件的,其他都加上锁 */ @Override protected void configure(HttpSecurity http) throws Exception { // 首页所有人可以访问,但是功能页只有对应有权限的人才能访问 http.authorizeRequests() .antMatchers("/", "/index.html", "/login.html", "/mainMenu.html", "/JsxsRoad.html", "/recommendedArticles.html", "/code", "/a3").permitAll() .antMatchers("/*").authenticated(); super.configure(http); http.formLogin().loginPage("/login.html"); // 登录配置 http.formLogin() .usernameParameter("username") // 这个参数必须为username (约定大于配置) .passwordParameter("password") // 这个参数必须为password (约定大于配置) .loginPage("/login.html") //登入页面是哪个? .loginProcessingUrl("/code") // 点击提交的时候跳转到哪里? .defaultSuccessUrl("/"); // 密码验证通过后我们跳转到哪里? // 注销配置 http.headers().contentTypeOptions().disable(); http.headers().frameOptions().disable(); // 图片跨域 http.csrf().disable();//关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求(ajax如果要使用一定开启) http.logout().logoutSuccessUrl("/"); } /** * * @param auth * @throws Exception userDetailsService-》对哪个业务进行操作,passwordEncoder-》密码加密方式 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(ckqnUserService).passwordEncoder(passwordEncoder()); } /** * * @param web * @throws Exception 对静态资源不拦截: static目录可以省略 */ @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/css/**", "/assets/**", "/plugins/**", "/img/**", "/editor.md/**", "/Jquery/**", "/layui/**", "/qrcode/**"); } }
5.接口实现类 CkqnUserServiceImpl
CkqnUserServiceImpl
package com.jsxs.kumquat.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.jsxs.kumquat.pojo.CkqnUser; import com.jsxs.kumquat.mapper.CkqnUserMapper; import com.jsxs.kumquat.pojo.CkqnUserRole; import com.jsxs.kumquat.service.CkqnUserRoleService; import com.jsxs.kumquat.service.CkqnUserService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import javax.annotation.Resource; import javax.servlet.http.HttpSession; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * <p> * 服务实现类 * </p> * * @author 吉士先生 * @since 2023-04-09 * TODO 这里需要继承一个接口: UserDetailsService */ @Service public class CkqnUserServiceImpl extends ServiceImpl<CkqnUserMapper, CkqnUser> implements CkqnUserService, UserDetailsService { // 1. 业务层接口 @Resource CkqnUserService userService; // 2. 认证角色层接口 @Resource CkqnUserRoleService roleService; // 3.session @Resource HttpSession session; /** * TODO 用户逻辑和认证 * * @param uid * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String uid) throws UsernameNotFoundException { // 4. 通过用户名查询用户 CkqnUser user = userService.getOne(new QueryWrapper<CkqnUser>().eq("uid", uid)); // 5. 查询到的角色放入session session.setAttribute("loginUser", user); // 7.创建一个新的UserDetails对象,最后验证登陆的需要 UserDetails userDetails = null; if (user != null) { System.out.println("未加密:" + user.getPassword()); // 8.加密密码 String BCryptPassword = new BCryptPasswordEncoder().encode(user.getPassword()); // 登录后会将登录密码进行加密,然后比对数据库中的密码,数据库密码需要加密存储! // String password = user.getPassword(); // 9. 创建一个集合来存放权限 Collection<GrantedAuthority> authorities = getAuthorities(user); /** * TODO 实例化UserDetails对象 * uid-> 前端username框传过来的数据 * BCryptPassword-> 加密后的密码 * authorities -》 认证列表 */ userDetails = new org.springframework.security.core.userdetails.User(uid, BCryptPassword, true, true, true, true, authorities); } return userDetails; } /** * @param user ->实体类 * @return */ private Collection<GrantedAuthority> getAuthorities(CkqnUser user) { List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(); // 1. 通过用户的第三个字段roleID再另一个表中查找数据据 CkqnUserRole role = roleService.getById(user.getRoleId()); // 2. 注意:这里每个权限前面都要加ROLE_。否在最后验证不会通过 authList.add(new SimpleGrantedAuthority("ROLE_" + role.getName())); return authList; } }
两张表
6.前端认证问题
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
sec:authorize="!isAuthenticated()" 没有被认证就是显示 sec:authorize="isAuthenticated()" 认证了才被显示
(二)、SpringBoot整合Ajax
1.登入表单
<form th:action="@{/code}" method="post"> <div class="inputBox"> <input type="text" placeholder="账户ID" name="username" id="name" required onblur="a1()" onkeyup="this.value=this.value.replace(/\s+/g,'')"> <--*******id 为username, 上面id为name--> <i id="username" style="z-index: 9999"></i> </div> <div class="inputBox"> <input type="password" placeholder="密码" name="password" required onkeyup="this.value=this.value.replace(/\s+/g,'')"> </div> <div class="inputBox"> <input type="submit" value="登录"> </div> <p class="forget">忘记密码?<a href="#"> 点击这里 </a></p> <p class="forget">没有账户?<a href="#"> 注册 </a></p> </form>
2. JavaScript
// AJAX -js <script src="Jquery/jquery-3.6.1.js"></script> function a1(){ $.post({ url:"http://localhost:8080/a3", data:{'name':$("#name").val()}, success:function(data){ if(data.toString()=='OK'){ $("#username").css("color","green"); }else{ $("#username").css("color","red"); } $("#username").html(data); } }); } function a2(){ $.post({ url: "http://localhost:8080/a3", data: {'pwd': $("#pwd").val()}, success: function (data) { if (data.toString()=='OK') { $("#userpwd").css("color", "green"); } else { $("#userpwd").css("color", "red"); } $("#userpwd").html(data); } }); }
(三)、SpringBoot整合editor.md
前提需要我们导入editor.md的插件文件
1.编写页面
(1).前端页面
引入文件
<link rel="stylesheet" href="{/editor.md/examples/css/style.css" th:href="@{/editor.md/examples/css/style.css/}"> <link rel="stylesheet" href="/editor.md/css/editormd.css" th:href="@{/editor.md/css/editormd.css}"/> <link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon"/> <script src="Jquery/jquery-3.6.1.js"></script>
在这里编写文件
<div id="test-editormd"> <textarea style="display:none;" name="content"></textarea> </div>
配置JS
<script src="/editor.md/examplesjs/jquery.min.js" th:src="@{/editor.md/examples/js/jquery.min.js}"></script> <script src="/editor.md/editormd.js" th:src="@{/editor.md/editormd.js}"></script> <script type="text/javascript"> var testEditor; $(function () { $.get('/editor.md/examples/test.md', function (md) { // 这里的第一个参数,指的是:"编写页面的id": 第二个参数是: "配置" testEditor = editormd("test-editormd", { width: "100%", // 1.宽度 height: "750px", // 2.高度 path: '/editor.md/lib/', //3.配置文件 theme: "dark", //4.主题 markdown:'#欢迎您来到金橘社区!!', //5.默认编辑页面 // previewTheme: "dark", editorTheme: "pastel-on-dark", // 6.编辑页面主题 imageUpload: true, //7.文件上传 imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp"], imageUploadURL: "/article/image/upload", //8.图片上传URL toolbarIcons: function () { //9.组件 return ["undo", "redo", "|", "bold", "del", "italic", "quote", "ucwords", "uppercase", "lowercase", "|", "h1", "h2", "h3", "h4", "h5", "h6", "|", "list-ul", "list-ol", "hr", "|", "link", "reference-link", "image", "code", "preformatted-text", "code-block", "table", "datetime", "emoji", "html-entities", "pagebreak", "|", "goto-line", "watch", "preview", "fullscreen", "clear", "search", "|", "help", "info", "||", "publish"]; }, toolbarIconTexts: { //10.自定义发布按钮的组件 publish: "<span bgcolor='gray' style='font-family: 微软雅黑;font-weight: bold;color: #00FF00'>发布</span>" }, toolbarHandlers: { //11. 提交文章上传的路径 publish: function (cm, icon, cursor, selection) { mdEditorForm.method = "post"; mdEditorForm.action = "/article/publish";//提交至服务器的路径 mdEditorForm.submit(); } } }); }); }); </script>
全部文档信息
<!DOCTYPE html> <html lang="zh" xmlns:sec="http://www.thymeleaf.org/extras/spring-security"> <head> <meta charset="utf-8"/> <title>金橘社区-文章编写</title> <link rel="stylesheet" href="{/editor.md/examples/css/style.css" th:href="@{/editor.md/examples/css/style.css/}"> <link rel="stylesheet" href="/editor.md/css/editormd.css" th:href="@{/editor.md/css/editormd.css}"/> <link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon"/> <link rel="stylesheet" href="layui/css/layui.css" media="all"> <script src="layui/layui.js" charset="utf-8"></script> <script src="Jquery/jquery-3.6.1.js"></script> <style> * { margin: 0; padding: 0; } .top-search { width: 680px; height: 45px; margin: 30px auto; } .search-box { display: flex; position: relative; } .search-left { width: 545px; height: 45px; border: 2px solid rgb(196, 199, 206); border-top-left-radius: 10px; border-bottom-left-radius: 10px; outline-color: rgb(242, 78, 130); } .icon-xiangji { position: absolute; right: 150px; top: 12px; font-size: 24px; color: rgb(196, 199, 206); } .search-right { color: #fff; font-size: 18px; width: 110px; height: 49px; border: 0px; border-top-right-radius: 10px; border-bottom-right-radius: 10px; background-color: rgb(242, 78, 130); } #su:hover { background: #14dc99; } </style> </head> <body> <form name="mdEditorForm" method="post"> <div class="top-search" style="margin-top: 13px;"> <button type="button" class="layui-btn layui-btn-primary1 layui-btn-lg" style="margin-top: 7px;margin-left: -1200px;" onclick="document.location.href='MyArticals.html'"><i class="layui-icon layui-icon-return" style="font-size: 20px; color: #1E9FFF;"></i><i style="color: #0e0c0d;font-family: 微软雅黑;font-weight: bold" onclick="return confirm('您还未保存,如果直接退出将不会保存数据。您确定直接退出?')">文章管理</i></button> <div class="search-box" style=" top: -45px;"> <input type="text" name="title" class="search-left" placeholder=" 请 输 入 文 章 标 题" required style="font-size: 20px;font-family: 微软雅黑;font-weight: bold;" onkeyup="this.value=this.value.replace(/\s+/g,'')" value="【无标题】"> <span class="iconfont icon-xiangji"></span> </div> <ul sec:authorize="!isAuthenticated()" class="layui-nav" lay-bar="disabled" style="width: 40px; height: 0px;margin-left: 863px;margin-top: -100px;z-index: 9999"> <li class="layui-nav-item" lay-unselect=""> <a href="javascript:;"><img src="/img/login.png" tppabs="http://t.cn/RCzsdCq" class="layui-nav-img" onclick="document.location.href='login.html'"></a> <dl class="layui-nav-child"> <dd><a href="javascript:;" onclick="document.location.href='login.html'">登入</a></dd> </dl> </li> </ul> <ul sec:authorize="hasRole('ROLE_A')" class="layui-nav" lay-bar="disabled" style="width: 40px; height: 0px;margin-left: 863px;margin-top: -100px;z-index: 9999"> <li class="layui-nav-item" lay-unselect=""> <a href="javascript:;"><img src="/img/user.jpg" tppabs="http://t.cn/RCzsdCq" class="layui-nav-img" onclick="document.location.href='#'"></a> <dl class="layui-nav-child"> <dd><a href="javascript:;">个人中心</a></dd> <dd><a href="javascript:;" th:href="@{/login.html}">切换账号</a></dd> <dd><a href="javascript:;" th:href="@{/logout}">注销</a></dd> </dl> </li> </ul> <ul class="layui-nav" lay-bar="disabled" style="width: 40px; height: 0px;margin-left: 863px;margin-top: -100px;z-index: 9999"> <li sec:authorize="hasRole('ROLE_B')" class="layui-nav-item" lay-unselect=""> <a href="javascript:;"><img src="/img/user2.jpg" tppabs="http://t.cn/RCzsdCq" class="layui-nav-img" onclick="document.location.href='#'"></a> <dl class="layui-nav-child"> <dd><a href="javascript:;">个人中心</a></dd> <dd><a href="javascript:;" onclick="document.location.href='login.html'">切换账号</a></dd> <dd><a href="javascript:;" th:href="@{/logout}">注销</a></dd> </dl> </li> </ul> </div> <div id="layout" style="margin-top: -41px;"> <header> </header> <div id="test-editormd"> <textarea style="display:none;" name="content"></textarea> </div> </div> </form> <script src="/editor.md/examplesjs/jquery.min.js" th:src="@{/editor.md/examples/js/jquery.min.js}"></script> <script src="/editor.md/editormd.js" th:src="@{/editor.md/editormd.js}"></script> <script type="text/javascript"> var testEditor; $(function () { $.get('/editor.md/examples/test.md', function (md) { testEditor = editormd("test-editormd", { width: "100%", height: "750px", path: '/editor.md/lib/', theme: "dark", markdown:'#欢迎您来到金橘社区!!', // previewTheme: "dark", editorTheme: "pastel-on-dark", imageUpload: true, imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp"], imageUploadURL: "/article/image/upload", toolbarIcons: function () { return ["undo", "redo", "|", "bold", "del", "italic", "quote", "ucwords", "uppercase", "lowercase", "|", "h1", "h2", "h3", "h4", "h5", "h6", "|", "list-ul", "list-ol", "hr", "|", "link", "reference-link", "image", "code", "preformatted-text", "code-block", "table", "datetime", "emoji", "html-entities", "pagebreak", "|", "goto-line", "watch", "preview", "fullscreen", "clear", "search", "|", "help", "info", "||", "publish"]; }, toolbarIconTexts: { publish: "<span bgcolor='gray' style='font-family: 微软雅黑;font-weight: bold;color: #00FF00'>发布</span>" }, toolbarHandlers: { publish: function (cm, icon, cursor, selection) { mdEditorForm.method = "post"; mdEditorForm.action = "/article/publish";//提交至服务器的路径 mdEditorForm.submit(); } } }); }); }); </script> </body> </html>