UEditor 已经停止维护,最后版本停留在16年,但胜在插件多,所有一直在使用。
UEditor的核心控制器是JSP写的,但是SpringBoot抛弃了JSP,导致兼容性出现很大的问题。
所以需要将Ueditor改造一番,让其能与SpringBoot兼容。
- 导入Ueditor的依赖
<!--UEditor依赖的jar包 -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180130</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
- 将uedito的jar包解开,放入工程
- 解开之后,就需要修改一些方法
ConfigManager是读取配置文件的类,修改getConfigPath方法,让其可以顺利读取到config.json,我这里将config.json放到了resources目录下。
private String getConfigPath() {
try {
//获取classpath下的config.json路径
String path = this.getClass().getClassLoader().getResource("config.json").getPath();
System.out.println(path);
return path;
} catch (Exception e) {
return null;
}
}
BinaryUploader是文件上传类,修改save方法
public static final State save(HttpServletRequest request, Map<String, Object> conf) {
if (!ServletFileUpload.isMultipartContent(request)) {
return new BaseState(false, 5);
} else {
try {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
MultipartFile multipartFile = multipartRequest.getFile(conf.get("fieldName").toString());
if(multipartFile==null){
return new BaseState(false, AppInfo.NOTFOUND_UPLOAD_DATA);
}
String savePath = (String) conf.get("savePath");
String originFileName = multipartFile.getOriginalFilename();
String suffix = FileType.getSuffixByFilename(originFileName);
originFileName = originFileName.substring(0,
originFileName.length() - suffix.length());
savePath = savePath + suffix;
long maxSize = ((Long) conf.get("maxSize")).longValue();
if (!validType(suffix, (String[]) conf.get("allowFiles"))) {
return new BaseState(false, AppInfo.NOT_ALLOW_FILE_TYPE);
}
savePath = PathFormat.parse(savePath, originFileName);
String basePath=(String) conf.get("basePath");
String physicalPath = basePath + savePath;
InputStream is = multipartFile.getInputStream();
State storageState = StorageManager.saveFileByInputStream(is,
physicalPath, maxSize);
is.close();
if (storageState.isSuccess()) {
storageState.putInfo("url", PathFormat.format(savePath));
storageState.putInfo("type", suffix);
storageState.putInfo("original", originFileName + suffix);
}
return storageState;
} catch (IOException var15) {
return new BaseState(false, 4);
}
}
}
在上面用到一个config.json没有的参数basePath,所有需要config.json添加该参数,指向文件保存的路径
- 新建一个controller,替代原有的controller.jsp
import com.baidu.ueditor.ActionEnter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Controller
@RequestMapping("/")
public class UEditorController {
@RequestMapping(value="/config")
public void config(HttpServletRequest request, HttpServletResponse response) {
response.setContentType("application/json");
String rootPath = request.getSession().getServletContext().getRealPath("/");
try {
String exec = new ActionEnter(request, rootPath).exec();
PrintWriter writer = response.getWriter();
writer.write(exec);
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
导入ueditor的相关文件
更改ueditor.config.js访问的controller
修改application.yml配置文件
spring:
resources:
static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,file:${file.upload.savePath}
mvc:
static-path-pattern: /**
file:
upload:
savePath: /User/Qianyuan/Ueditor/File/
download:
url: http://127.0.0.1:8891/
- 页面引入并初始化
<body>
...
<script id="container" name="content" type="text/plain"></script>
...
</body>
...
<script src="/ueditor/ueditor.config.js"></script>
<script src="/ueditor/ueditor.all.js"></script>
<script>
var ue = UE.getEditor('container');
</script>
如果正确配置config.json中的imageUrlPrefix 和 imagePathFormat,此时启动项目,ueditor已经可以正常使用,但是打包后无法正确读取配置文件,并且配置不太方便,所以准备移除config.json文件
- 新建配置文件类
public static String path;
public static String url;
public static String config = "{\n" +
" \"imageActionName\": \"uploadimage\",\n" +
" \"imageFieldName\": \"upfile\",\n" +
" \"imageMaxSize\": 2048000,\n" +
" \"imageAllowFiles\": [\n" +
" \".png\",\n" +
" \".jpg\",\n" +
" \".jpeg\",\n" +
" \".gif\",\n" +
" \".bmp\"\n" +
" ],\n" +
" \"imageCompressEnable\": true,\n" +
" \"imageCompressBorder\": 1600,\n" +
" \"imageInsertAlign\": \"none\",\n" +
" \"imageUrlPrefix\": \"\",\n" +
" \"imagePathFormat\": \"image/{yyyy}{mm}{dd}/{time}{rand:6}\",\n" +
" \"scrawlActionName\": \"uploadscrawl\",\n" +
" \"scrawlFieldName\": \"upfile\",\n" +
" \"scrawlPathFormat\": \"image/{yyyy}{mm}{dd}/{time}{rand:6}\",\n" +
" \"scrawlMaxSize\": 2048000,\n" +
" \"scrawlUrlPrefix\": \"\",\n" +
" \"scrawlInsertAlign\": \"none\",\n" +
" \"snapscreenActionName\": \"uploadimage\",\n" +
" \"snapscreenPathFormat\": \"image/{yyyy}{mm}{dd}/{time}{rand:6}\",\n" +
" \"snapscreenUrlPrefix\": \"\",\n" +
" \"snapscreenInsertAlign\": \"none\",\n" +
" \"catcherLocalDomain\": [\n" +
" \"127.0.0.1\",\n" +
" \"localhost\",\n" +
" \"img.baidu.com\"\n" +
" ],\n" +
" \"catcherActionName\": \"catchimage\",\n" +
" \"catcherFieldName\": \"source\",\n" +
" \"catcherPathFormat\": \"image/{yyyy}{mm}{dd}/{time}{rand:6}\",\n" +
" \"catcherUrlPrefix\": \"\",\n" +
" \"catcherMaxSize\": 2048000,\n" +
" \"catcherAllowFiles\": [\n" +
" \".png\",\n" +
" \".jpg\",\n" +
" \".jpeg\",\n" +
" \".gif\",\n" +
" \".bmp\"\n" +
" ],\n" +
" \"videoActionName\": \"uploadvideo\",\n" +
" \"videoFieldName\": \"upfile\",\n" +
" \"videoPathFormat\": \"video/{yyyy}{mm}{dd}/{time}{rand:6}\",\n" +
" \"videoUrlPrefix\": \"\",\n" +
" \"videoMaxSize\": 102400000,\n" +
" \"videoAllowFiles\": [\n" +
" \".flv\",\n" +
" \".swf\",\n" +
" \".mkv\",\n" +
" \".avi\",\n" +
" \".rm\",\n" +
" \".rmvb\",\n" +
" \".mpeg\",\n" +
" \".mpg\",\n" +
" \".ogg\",\n" +
" \".ogv\",\n" +
" \".mov\",\n" +
" \".wmv\",\n" +
" \".mp4\",\n" +
" \".webm\",\n" +
" \".mp3\",\n" +
" \".wav\",\n" +
" \".mid\"\n" +
" ],\n" +
" \"fileActionName\": \"uploadfile\",\n" +
" \"fileFieldName\": \"upfile\",\n" +
" \"filePathFormat\": \"file/{yyyy}{mm}{dd}/{time}{rand:6}\",\n" +
" \"fileUrlPrefix\": \"\",\n" +
" \"fileMaxSize\": 51200000,\n" +
" \"fileAllowFiles\": [\n" +
" \".png\",\n" +
" \".jpg\",\n" +
" \".jpeg\",\n" +
" \".gif\",\n" +
" \".bmp\",\n" +
" \".flv\",\n" +
" \".swf\",\n" +
" \".mkv\",\n" +
" \".avi\",\n" +
" \".rm\",\n" +
" \".rmvb\",\n" +
" \".mpeg\",\n" +
" \".mpg\",\n" +
" \".ogg\",\n" +
" \".ogv\",\n" +
" \".mov\",\n" +
" \".wmv\",\n" +
" \".mp4\",\n" +
" \".webm\",\n" +
" \".mp3\",\n" +
" \".wav\",\n" +
" \".mid\",\n" +
" \".rar\",\n" +
" \".zip\",\n" +
" \".tar\",\n" +
" \".gz\",\n" +
" \".7z\",\n" +
" \".bz2\",\n" +
" \".cab\",\n" +
" \".iso\",\n" +
" \".doc\",\n" +
" \".docx\",\n" +
" \".xls\",\n" +
" \".xlsx\",\n" +
" \".ppt\",\n" +
" \".pptx\",\n" +
" \".pdf\",\n" +
" \".txt\",\n" +
" \".md\",\n" +
" \".xml\"\n" +
" ],\n" +
" \"imageManagerActionName\": \"listimage\",\n" +
" \"imageManagerListPath\": \"/ueditor/jsp/upload/image/\",\n" +
" \"imageManagerListSize\": 20,\n" +
" \"imageManagerUrlPrefix\": \"\",\n" +
" \"imageManagerInsertAlign\": \"none\",\n" +
" \"imageManagerAllowFiles\": [\n" +
" \".png\",\n" +
" \".jpg\",\n" +
" \".jpeg\",\n" +
" \".gif\",\n" +
" \".bmp\"\n" +
" ],\n" +
" \"fileManagerActionName\": \"listfile\",\n" +
" \"fileManagerListPath\": \"file/\",\n" +
" \"fileManagerUrlPrefix\": \"\",\n" +
" \"fileManagerListSize\": 20,\n" +
" \"fileManagerAllowFiles\": [\n" +
" \".png\",\n" +
" \".jpg\",\n" +
" \".jpeg\",\n" +
" \".gif\",\n" +
" \".bmp\",\n" +
" \".flv\",\n" +
" \".swf\",\n" +
" \".mkv\",\n" +
" \".avi\",\n" +
" \".rm\",\n" +
" \".rmvb\",\n" +
" \".mpeg\",\n" +
" \".mpg\",\n" +
" \".ogg\",\n" +
" \".ogv\",\n" +
" \".mov\",\n" +
" \".wmv\",\n" +
" \".mp4\",\n" +
" \".webm\",\n" +
" \".mp3\",\n" +
" \".wav\",\n" +
" \".mid\",\n" +
" \".rar\",\n" +
" \".zip\",\n" +
" \".tar\",\n" +
" \".gz\",\n" +
" \".7z\",\n" +
" \".bz2\",\n" +
" \".cab\",\n" +
" \".iso\",\n" +
" \".doc\",\n" +
" \".docx\",\n" +
" \".xls\",\n" +
" \".xlsx\",\n" +
" \".ppt\",\n" +
" \".pptx\",\n" +
" \".pdf\",\n" +
" \".txt\",\n" +
" \".md\",\n" +
" \".xml\"\n" +
" ]\n" +
"}";
- 添加启动任务,加载属性配置
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Order(2)
@Component
public class Mission implements CommandLineRunner {
@Value("${file.upload.savePath}")
private String path;
@Value("${file.download.url}")
private String url;
@Override
public void run(String... args) throws Exception {
UEditorProperties.path = path;
UEditorProperties.url = url;
}
}
- 修改ConfigManager的initEnv方法
private void initEnv() {
File file = new File(this.originalPath);
if (!file.isAbsolute()) {
file = new File(file.getAbsolutePath());
}
this.parentPath = file.getParent();
String configContent = UEditorProperties.config;
try {
JSONObject jsonConfig = new JSONObject(configContent);
this.jsonConfig = jsonConfig;
} catch (Exception var4) {
this.jsonConfig = null;
}
}
- 修改UEditorController的config方法
@RequestMapping(value="/config")
public void config(HttpServletRequest request, HttpServletResponse response) {
response.setContentType("application/json");
String rootPath = request.getSession().getServletContext().getRealPath("/");
try {
String exec = new ActionEnter(request, rootPath).exec();
JSONObject object = JSONObject.parseObject(exec);
object.put("basePath",UEditorProperties.path);
object.put("imageUrlPrefix",UEditorProperties.url);
exec = JSON.toJSONString(object);
PrintWriter writer = response.getWriter();
writer.write(exec);
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
至此 ,SpringBoot和UEditor的整合完成
开始整合Security
- 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
- 新建Security的配置
import com.megvii.cms.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启security注解
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.headers()
.addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN))
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/image/**", "/css/**", "/js/**", "/api/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/")
.failureUrl("/login")
.permitAll()
.and()
.rememberMe()
.tokenValiditySeconds(1209600)
.key("mykey")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.permitAll();
}
.headers().addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN))
.csrf().disable()
因为Ueditor兼容性问题,所以必须加上
- 导入thymeleaf依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
- 配置静态页面模板位置
spring:
thymeleaf:
prefix: classpath:/templates
suffix: .html
- 新建登录页面
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Megvii-CMS-登录</title>
<link href="/css/bootstrap.min.css" rel="stylesheet">
<link href="/css/login/signin.css" rel="stylesheet">
</head>
<body class="text-center">
<form class="form-signin" action='/login' method='POST' >
<h1 class="h3 mb-3 font-weight-normal">Megvii-CMS</h1>
<input type="username" name="username" class="form-control" placeholder="用户名或邮箱">
<input type="password" name="password" class="form-control" placeholder="密码">
<button class="btn btn-lg btn-primary btn-block" type="submit">登录</button>
</form>
</body>
</html>
- 新建Controller指向login.html
@Controller
@RequestMapping("/")
public class WebController {
@GetMapping("/login")
public String login(){
return "/login";
}
}
实现userDetailsService里的loadUserByUsername()方法
略
退出登录
<a href="/logout">退出登录</a>
至此,完成SpringBoot + Spring Security + UEditor完整整合,UEditor各项功能正常使用,Security验证正常,打包后运行正常