目录
1. 基本介绍
1.1技术栈
后台:
- SpringBoot
- Spring
- Mybatis
- MybatisPlus
- SpringSecurity
- Lombok
- Maven
- Druid
- swagger
前端:
- thymeleaf模板引擎
- jquery html css js
- editormd MarkDown插件
- font-awesome图标包
- bootstrap
- webjar的maven引用
2. 后台实现及问题
2.1 MybatisPlus代码生成
使用MybatisPlus自动生成Controller Service Mapper 与Xml。
/**
* 代码自动生成 MybatisPlus
* @author 17566
* @Date: 2020/8/19 17:28
*/
public class GenerateCode {
public static void main(String[] args) {
// 需要构建一个 代码自动生成器 对象
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 配置策略
// 1、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath+"/src/main/java");
gc.setAuthor("whai");
gc.setOpen(false);
// 是否覆盖
gc.setFileOverride(false);
// 去Service的I前缀
gc.setServiceName("%sService");
//雪花算法加密id
gc.setIdType(IdType.UUID);
gc.setDateType(DateType.ONLY_DATE);
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
//2、设置数据源
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/whaiblog?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
//3、包的配置
PackageConfig pc = new PackageConfig();
// pc.setModuleName("blog");
pc.setParent("com.whai");
pc.setEntity("model");
pc.setMapper("mapper");
pc.setService("service");
pc.setServiceImpl("service.impl");
pc.setController("controller");
// pc.setXml()
mpg.setPackageInfo(pc);
//4、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("article","article_tag","comment","message","visit","user");
// 设置要映射的表名 下划线变驼峰命名
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
// 自动lombok;
strategy.setEntityLombokModel(true);
// 自动填充配置
// strategy.setLogicDeleteFieldName("deleted");
// TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
// TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE);
// ArrayList<TableFill> tableFills = new ArrayList<>();
// tableFills.add(gmtCreate);
// tableFills.add(gmtModified);
// strategy.setTableFillList(tableFills);
// 乐观锁
// strategy.setVersionFieldName("version");
// strategy.setRestControllerStyle(true);
// strategy.setControllerMappingHyphenStyle(true);
// localhost:8080/hello_id_2
mpg.setStrategy(strategy);
mpg.execute();
//执行
}
}
2.2 Controller调用MybatisPlus的Service
MybatisPlus的配置分页插件paginationInterceptor
/**
* @author 17566
* @Date: 2020/8/19 17:17
*/
@Configuration
public class MybatisPlusConfig {
/**
* 分页插件
* @return
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
/**
* SQL执行效率插件
* 性能分析拦截器,用于输出每条 SQL 语句及其执行时间
* // 设置 dev test 环境开启,保证我们的效率
* SQL执行效率插件
* */
@Bean
// @Profile({"dev","test"})
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
// performanceInterceptor.setMaxTime(100);
// ms设置sql执行的最大时间,如果超过了则不 执行
// performanceInterceptor.setFormat(true);
// 是否格式化代码
return performanceInterceptor;
}
}
page
分页插件
Page<Article> articlePage = new Page(long current, long size);
List<Article> articleList = articlePage.getRecords();
xxxWrapper 的使用
筛选过滤排序
QueryWrapper<Article> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("article_create_time");
问题
- 可优化时间的自动绑定,这个系统的很多时间还是new Date(),并不能满足实际的需要
可使用MyabtisPlus的自动绑定进行优化 - 对一些注解的使用还要多看看和使用
2.3 Swagger使用
/**
* @author 17566
* @Date: 2020/8/21 16:19
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Value("${swagger.enable}")
private Boolean enable;
//在Application.yml中再进行seagger: enable: false的配置
/**
* 配置docket以配置Swagger具体参数 api需要关联下面的info
* @return
*/
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(enable);
}
private ApiInfo apiInfo() {
Contact contact = new Contact("lkwhai@163.com", "whai", "/");
ApiInfo apiInfo = new ApiInfo("whaiBlog",
"whai的简单博客",
"v1.0",
"null",
contact,
null, null, new ArrayList<>());
return apiInfo;
}
}
关闭swagger
@Configuration
@EnableSwagger2
@Profile({"dev", "test"})
//只在tset dev下有效
问题
- 对一些注解的使用还要多看看和使用
2.4 SpringSecurity的使用
Configration
- 登陆页面的参数绑定 跳转
- 注销
- 验证时采用的加密方式
BCryptPasswordEncoder();
/**
* @author 17566
* @Date: 2020/8/19 11:17
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserServiceImpl userService;
//请求授权验证
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin*/**").hasRole("admin");
http.formLogin().loginPage("/toLogin")
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/login")
//login为form请求提交位置
/*<form action="${loginProcessingUrl}" method="post">*/
.defaultSuccessUrl("/admin/index");
// http.formLogin().usernameParameter("username")
// .passwordParameter("password").loginPage("/toLogin").loginProcessingUrl("/login").defaultSuccessUrl("/admin/index");
http.headers().contentTypeOptions().disable();
http.headers().frameOptions().disable(); // 图片跨域
http.csrf().disable();//关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求
http.logout().logoutSuccessUrl("/");
//
// // 记住我配置
http.rememberMe().rememberMeParameter("remember").alwaysRemember(true);
}
/**
* 用户自定义认证规则 登陆验证
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
// auth.inMemoryAuthentication()
// .withUser("user").password(passwordEncoder().encode("123")).roles("admin");
}
// 密码加密方式
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
UserService
- UserDetailsService的接口实现
- 角色信息获取 (在很多情况下需要到数据库中获取,本站局限于本用户,直接写入ROLE_admin
**
* <p>
* 服务实现类
* </p>
*
* @author whai
* @since 2020-08-20
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService,UserDetailsService{
@Autowired
UserService userService;
@Autowired
HttpSession session;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.getOne(new QueryWrapper<User>().eq("user_name", username));
session.setAttribute("loginUser",user);
UserDetails userDetails = null;
if (null!=user){
String password = user.getUserPass();
userDetails = new org.springframework.security.core.userdetails.User(
username,password,getAuthorities(user));
}
return userDetails;
}
/**
* 获取角色信息
*
* ROLE_前缀
* https://blog.csdn.net/universsky2015/article/details/77965567
* @param user
* @return
*/
private Collection<GrantedAuthority> getAuthorities(User user){
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
//注意:这里每个权限前面都要加ROLE_。否在最后验证不会通过
authList.add(new SimpleGrantedAuthority("ROLE_admin"));
return authList;
}
}
管理员用户修改密码的实现
-
SecurityContextHolder.getContext().getAuthentication()
获取Authentication
Authentication extends Principal
-
获取
authentication.getName();
-
根据username进行查询
-
BCryptPasswordEncoder bc = new BCryptPasswordEncoder();
boolean matches = bc.matches(currentInputPwd,userPass);
验证密码是否正确 -
验证成功,使用
BCryptPasswordEncoder().encode(newPwd)进行加密
/**
*https://blog.csdn.net/Mr_LiYyang/article/details/97312327
* 修改密码
* @param passwordForm
* @param model
* @return
*/
@PostMapping("/updatePassword")
public String updatePassword(PasswordForm passwordForm, Model model){
//用户输入的当前密码
String currentInputPwd = passwordForm.getCurrentPwd();
//新密码
String newPwd = passwordForm.getNewPwd();
//验证新密码
String verifyPwd = passwordForm.getVerifyPwd();
if (currentInputPwd.isEmpty()){
model.addAttribute("msg","请输入当前密码");
return "admin/information";
}else if (newPwd.isEmpty()){
model.addAttribute("msg","请输入新密码");
return "admin/information";
}else if (verifyPwd.isEmpty()){
model.addAttribute("msg","请再一次输入新密码");
return "admin/information";
}else if (!newPwd.equals(verifyPwd)){
model.addAttribute("msg","两次输入密码验证不一致");
return "admin/information";
}
//获取 Authentication域的用户名
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authentication.getName();
User sysUser = userService.getOne(new QueryWrapper<User>().eq("user_name", username));
//加密的原密码
String userPass = sysUser.getUserPass();
BCryptPasswordEncoder bc = new BCryptPasswordEncoder();
boolean matches = bc.matches(currentInputPwd,userPass);
if (matches){
//验证成功
sysUser.setUserPass(bc.encode(newPwd));
userService.updateById(sysUser);
model.addAttribute("msg", "修改成功");
}else {
model.addAttribute("msg","密码验证错误");
}
return "admin/information";
}
问题
对SpringSecurity的很多方法类的了解还不够 本blog仅仅能用,还需要进行巩固加强
2.5 监听器实现启动时自动加载
-
获取当前的web容器
//强转为webApplicationContext WebApplicationContext applicationContext = (WebApplicationContext) event.getApplicationContext(); ServletContext context = applicationContext.getServletContext();
/**
* https://blog.csdn.net/m0_37202351/article/details/86180998
* @author 17566
* @Date: 2020/8/26 10:34
*/
@Component
public class InitWebListener implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
ArticleService articleService;
@Autowired
ArticleTagService tagService;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
QueryWrapper<Article> queryWrapper = new QueryWrapper<>();
//根据时间降序
queryWrapper.orderByDesc("article_create_time");
//只需要封装最新的三个文章信息
Page<Article> page = new Page<>(1,3);
articleService.page(page, queryWrapper);
List<Article> records = page.getRecords();
//标签信息
List<ArticleTag> tagList = tagService.list(null);
//强转为webApplicationContext
WebApplicationContext applicationContext = (WebApplicationContext) event.getApplicationContext();
ServletContext context = applicationContext.getServletContext();
//进入直接将全局参数给了 包含三篇基础文章显示 网站统计
context.setAttribute("article0",records.get(0));
context.setAttribute("article1",records.get(1));
context.setAttribute("article2",records.get(2));
context.setAttribute("tagList",tagList);
context.setAttribute("defaultAvatar","img/weixin.jpg");
}
}
问题
只是启动时将最新三篇blog发布到ApplicationContext中,但是一旦发布新文章时,不能实施地显示最新的博客,只能显示原来的那三篇博客,缺少实时性,可以优化。
2.6 Web配置
- 添加上资源的上传映射路径与真实路径,实现markdown的图片本地文件能够进行上传
- 发布jar包运行的时候需要在jar包同目录下创建一个
upload
的文件夹用于存放上传图片文件
**
* @author 17566
* @Date: 2020/8/18 17:16
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// registry.addViewController("/haha").setViewName("index");
// registry.addViewController("/haha").setViewName("index.html");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
}
/**
* 文件上传资源的加载
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/upload/**")
.addResourceLocations("file:"+System.getProperty("user.dir")+"/upload/");
// .addResourceLocations("/static/**").addResourceLocations("classpath:/static/");
}
}
问题
- 可以了解下editorMd插件的功能
- 对配置类的理解还有加强了解的必要
总结
这个blog还有很多可以优化的点
希望能够把以前所学习的技术栈在这个博客上进行使用实践。
自己瞎写的公众号与博客
普通二本数据科学与大数据技术专业菜鸟一个,望各位大神多多指导!互相学习进步!
![](https://i-blog.csdnimg.cn/blog_migrate/70a6e9c39ea8e942aedae819e95ad5e9.png)