欢迎关注博主公众号「java大师」, 回复【dsblog】获取源码
一、效果图展示
一、网站前台
1、首页


2、内容页


3、最新文章页面

二、网站后台
1、登录页

2、首页

3、栏目管理



4、文章管理



三、后端(swagger-ui)

二、 确定需求方案
后台进行栏目和文章的编辑,编辑完后前台展示。
三、整体架构设计
- 前端基于Vue+ElementUi,使用WebStorm编辑器;
- 后端基于Springboot+SpringSecurity+MyBatisPlus+JWT+Redis实现,使用 IDEA 编辑器;
- 数据传输格式:JSON;
- 数据库:mysql
三、编码实现 (关键代码)
1、前端Vue代码 - main.js
import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import router from './router'
import store from './store'
import {initMenu} from '/src/utils/menus'
import {getUserInfo} from '/src/utils/http'
import 'font-awesome/css/font-awesome.css'
import mavonEditor from 'mavon-editor'
import 'mavon-editor/dist/css/index.css';
Vue.config.productionTip = false
Vue.use(ElementUI);
Vue.use(mavonEditor)
router.beforeEach((to,from,next)=>{
if(window.sessionStorage.getItem("token")){
initMenu(router,store);
console.log(router);
if(!window.sessionStorage.getItem('user')){
return getUserInfo().then(res=>{
window.sessionStorage.setItem('user',JSON.stringify(res));
next();
})
}
next();
}else{
if(to.path=='/'){
next();
}else{
next('/?redirect='+to.path);
}
}
})
new Vue({
el: '#app',
router,
store,
render: h => h(App),
}).$mount('#app')
2、前端JS代码 - 动态路由
import {
getNav
} from './http.js'
export const initMenu = (router, store) => {
if (store.state.routes.length > 0) {
return;
}
getNav().then(data => {
if (data) {
let fmtRoutes = formatRoutes(data);
router.addRoutes(fmtRoutes);
router.options.routes = fmtRoutes;
store.commit('initRoutes',fmtRoutes);
}
})
}
export const dealPath=(path=>{
return ((path.split('/')).length-1)>1?path.substring(path.indexOf("/"),path.lastIndexOf("/")):path.substring(path.indexOf("/"),path.length)
})
export const formatRoutes = (routes) => {
let fmtRoutes = [];
routes.forEach(router=>{
let{
path,
component,
name,
iconCls,
children
} = router;
if(children&&children instanceof Array){
children = formatRoutes(children);
}
let fmtRouter = {
path:path,
name:name,
iconCls:iconCls,
children:children,
component:()=>import(`../views${dealPath(path)}/${component}.vue`),
}
fmtRoutes.push(fmtRouter);
})
return fmtRoutes;
}
3、后端Java代码 SecurityConfig
package com.dsblog.server.config.security;
import com.dsblog.server.filter.CustomFilter;
import com.dsblog.server.filter.CustomUrlDecisionManager;
import com.dsblog.server.model.User;
import com.dsblog.server.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private IUserService userService;
@Autowired
private RestAuthorizationEntryPoint restAuthorizationEntryPoint;
@Autowired
private RestAccessDeniedHandler restAccessDeniedHandler;
@Autowired
private CustomFilter customFilter;
@Autowired
private CustomUrlDecisionManager customUrlDecisionManager;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
"/login",
"/logout",
"/css/**",
"/js/**",
"/index.html",
"favicon.ico",
"/doc.html",
"/webjars/**",
"/swagger-resources/**",
"/v2/api-docs/**",
"/captcha",
"/ws/**",
"/static/**",
"/api/**"
);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.anyRequest().authenticated()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setAccessDecisionManager(customUrlDecisionManager);
o.setSecurityMetadataSource(customFilter);
return o;
}
})
.and()
.headers()
.cacheControl();
http.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
http.exceptionHandling()
.accessDeniedHandler(restAccessDeniedHandler)
.authenticationEntryPoint(restAuthorizationEntryPoint);
}
@Override
@Bean
public UserDetailsService userDetailsService(){
return username -> {
User user = userService.getUserByUsername(username);
if(null!=user){
user.setRoles(userService.getRoles(user.getId()));
return user;
}
throw new UsernameNotFoundException("用户名或密码不正确");
};
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){
return new JwtAuthenticationTokenFilter();
}
@Bean
public CorsConfigurationSource corsConfigurationSource(){
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:8080"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",configuration);
return source;
}
}
4、后端Java代码 swagger配置
package com.dsblog.server.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.pathMapping("/")
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.dsblog.server.controller"))
.paths(PathSelectors.any())
.build()
.securityContexts(securityContexts())
.securitySchemes(securitySchemes());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("dsblog接口文档")
.contact(new Contact("java大师","http://localhost:8081/doc.html","fry000@qq.com"))
.version("1.0").description("dsblog接口文档").build();
}
private List<SecurityContext> securityContexts() {
List<SecurityContext> result = new ArrayList<>();
result.add(getContextByPath("/.*"));
return result;
}
private SecurityContext getContextByPath(String pathRegex) {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex(pathRegex))
.build();
}
private List<SecurityReference> defaultAuth() {
List<SecurityReference> result = new ArrayList<>();
AuthorizationScope authorizationScope = new AuthorizationScope("global",
"accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
result.add(new SecurityReference("Authorization", authorizationScopes));
return result;
}
private List<ApiKey> securitySchemes() {
List<ApiKey> result = new ArrayList<>();
ApiKey apiKey = new ApiKey("Authorization", "Authorization", "header");
result.add(apiKey);
return result;
}
}
# 四、开发配置&代码结构说明
## 1、Java开发环境配置



然后GENERATE,下载包即可。
## 2、代码结构说明
### a、项目路径

### **b、application.yml配置**

|