首先创建一个maven
导入依赖坐标
com.github.pagehelper
pagehelper
5.2.0
com.auth0
java-jwt
3.11.0
org.springframework.security
spring-security-config
5.7.3
org.springframework.security
spring-security-web
5.7.3
cn.hutool
hutool-all
5.8.16
redis.clients
jedis
3.3.0
org.springframework.data
spring-data-redis
2.3.0.RELEASE
org.apache.commons
commons-pool2
2.8.0
org.mybatis
mybatis
3.5.6
org.mybatis
mybatis-spring
1.3.0
com.fasterxml.jackson.core
jackson-databind
2.9.4
org.springframework
spring-test
5.2.10.RELEASE
org.slf4j
slf4j-api
1.7.27
org.slf4j
slf4j-simple
1.7.27
org.projectlombok
lombok
1.18.24
provided
javax.servlet
javax.servlet-api
3.1.0
provided
org.springframework
spring-webmvc
5.3.18
org.springframework
spring-jdbc
5.3.30
com.alibaba
druid
1.1.16
mysql
mysql-connector-java
8.0.33
org.springframework
spring-tx
5.3.30
org.springframework
spring-test
5.3.30
junit
junit
4.12
test
com.fasterxml.jackson.core
jackson-databind
2.14.1
org.apache.poi
poi
4.1.2
commons-fileupload
commons-fileupload
1.3.3
commons-io
commons-io
2.4
com.google.code.gson
gson
2.8.9
编写配置类,首先配置springconfig配置文件
@Configuration
@ComponentScan({"com.kk"})
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class, MybatisConfig.class,JedisConfig.class})
// 开启Spring注解版事务功能
@EnableTransactionManagement
public class SpringConfig {
@Bean
public ObjectMapper getObjectMapper(){
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper;
}
}
编写jdbc.properties文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/sys_sun?serverTimezone=GMT%2B8
jdbc.username=root
jdbc.password=password
编写jdbcConfig
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
// 定义一个Bean,用于创建和配置Druid数据源对象
@Bean
public DataSource dataSource(){
// 创建DruidDataSource对象,这是一个用于数据库连接的数据源
DruidDataSource druidDataSource = new DruidDataSource();
// 设置数据库驱动类名,这是连接数据库所必需的
druidDataSource.setDriverClassName(driver);
// 设置数据库的URL,包括数据库类型和数据库名等
druidDataSource.setUrl(url);
// 设置数据库的用户名
druidDataSource.setUsername(username);
// 设置数据库的密码
druidDataSource.setPassword(password);
// 返回配置好的DruidDataSource对象
return druidDataSource;
}
// 定义一个Bean,用于创建平台事务管理器
@Bean
public PlatformTransactionManager platformTransactionManager(DataSource dataSource){
// 创建DataSourceTransactionManager对象,这是一个用于管理事务的组件
DataSourceTransactionManager ds = new DataSourceTransactionManager();
// 设置数据源,这是事务管理器管理事务的基础
ds.setDataSource(dataSource);
// 返回配置好的DataSourceTransactionManager对象
return ds;
}
}
配置mybatis
/**
* SqlSessionFactoryBean用于创建一个MyBatis的SqlSessionFactory对象,该对象用于生成与数据库的连接以及执行SQL操作。
* MapperScannerConfigurer用于配置MyBatis的Mapper接口,它会扫描指定的包路径,并为包内的接口自动创建代理对象,以便进行数据库操作。
*/
public class MybatisConfig {
// 创建一个SqlSessionFactoryBean的Bean,用于生成SqlSessionFactory对象
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws Exception {
// 创建SqlSessionFactoryBean实例
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// 设置数据源,用于连接数据库
sqlSessionFactoryBean.setDataSource(dataSource);
// 设置分页插件
PageInterceptor pageInterceptor = new PageInterceptor();
// pageInterceptor.setProperties(new Properties());
sqlSessionFactoryBean.setPlugins(new Interceptor[]{pageInterceptor});
// 设置类型别名所在的包路径,这样MyBatis就可以在该路径下查找对应的实体类
sqlSessionFactoryBean.setTypeAliasesPackage("com.kk.entity");
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/**/*.xml"));
// 返回SqlSessionFactoryBean实例
return sqlSessionFactoryBean;
}
// 创建一个MapperScannerConfigurer的Bean,用于自动扫描并注册Mapper接口
@Bean
private MapperScannerConfigurer mapperScannerConfigurer(){
// 创建MapperScannerConfigurer实例
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
// 设置Mapper接口所在的基包,MyBatis会扫描这个包下的所有接口,并创建对应的代理对象
mapperScannerConfigurer.setBasePackage("com.kk.mapper");
// 返回MapperScannerConfigurer实例
return mapperScannerConfigurer;
}
}
配置redis
@Configuration
public class JedisConfig {
@Bean
public RedisTemplate restTemplate() {
// 配置连接池
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// 最大空闲数
jedisPoolConfig.setMaxIdle(50);
// 最大连接数
jedisPoolConfig.setMaxTotal(100);
// 最大等待亳秒数
jedisPoolConfig.setMaxWaitMillis(20000);
// Redis 连接服务器配置
RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
redisConfig.setHostName("127.0.0.1");
redisConfig.setPort(6379);
redisConfig.setPassword("");
redisConfig.setDatabase(0);
// 修改 Redis 的连接器 Jedis 的默认连接池
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder builder = (JedisClientConfiguration.JedisPoolingClientConfigurationBuilder) JedisClientConfiguration.builder();
JedisClientConfiguration jedisClientConfiguration = builder.poolConfig(jedisPoolConfig).build();
// 采用 Jedis 作为 Redis 客户端,可用用 JedisConnectionFactory 工厂初始化连接池配置
JedisConnectionFactory connectionFactory = new JedisConnectionFactory(redisConfig,jedisClientConfiguration);
// 调用后初始化方法,没有它将抛出异常
connectionFactory.afterPropertiesSet();
// 定义 RedisTemplate,并设置连接工程
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(connectionFactory);
// 设置自定 Redis 序列化器
RedisSerializer
配置springMVCconfig
@Configuration
@ComponentScan({"com.kk.controller","com.kk.config"})
@EnableWebMvc
@EnableWebSecurity
public class SpringMvcConfig {
@Bean
public CommonsMultipartResolver multipartResolver(){
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
commonsMultipartResolver.setMaxUploadSize(10485760);
return commonsMultipartResolver;
}
}
编写webInit文件,可以替代web.xml文件
/**
* web工程的初始化类,代替web.xml
*/
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 指定Spring配置类
* @return
*/
protected Class>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class, WebSecurityConfig.class};
}
/**
* 指定SpringMVC配置类
* @return
*/
protected Class>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
/**
* 指定SpringMVC的映射规则,及url-pattern
* @return
*/
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 中文乱码处理
* @return
*/
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
return new Filter[]{characterEncodingFilter};
}
}
配置springSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Slf4j
@EnableWebSecurity
@Configuration
@EnableWebMvc
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AppAuthenticationSuccessHandler appAuthenticationSuccessHandler;
@Autowired
private AppAuthenticationFailHandler appAuthenticationFailHandler;
@Autowired
private AppLogoutSuccessHandler appLogoutSuccessHandler;
@Autowired
private AppAccessDenyHandler appAccessDenyHandler;
@Autowired
private JwtCheckFilter jwtCheckFilter;
@Autowired
private CrosFilter crosFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(crosFilter, UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(jwtCheckFilter, UsernamePasswordAuthenticationFilter.class);
//所有请求,都需要认证
http.authorizeRequests()
.antMatchers("/code/image","/start/doLogin","/menu/myMenu","/start/register","/files/pictures"
,"/users/auditUser/info","/code/check","/collage/info","/activity/entry","/inform/all",
"/inform/getOne","/inform/type")
.anonymous() //放开验证码的请求
.anyRequest()
.authenticated();
http.formLogin()
.successHandler(appAuthenticationSuccessHandler) //配置认证成功处理器
.failureHandler(appAuthenticationFailHandler) //配置认证失败处理器
.permitAll();
http.logout().logoutSuccessHandler(appLogoutSuccessHandler); //配置退出成功处理器
http.exceptionHandling().accessDeniedHandler(appAccessDenyHandler); //配置拒绝访问处理器
//不创建session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.csrf().disable();
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
/**
* AuthenticationManager
*
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception
{
return super.authenticationManagerBean();
}
}
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
/*
spring或者springmvc中会自动将此类注入到父类中
若当前环境没有使用Spring或SpringMVC,则需要将WebSecurityConfig(SpringSecurity配置类)传入超类,以确保获取配置,并创建springcontext
public SecurityWebApplicationInitializer() {
super(WebSecurityConfig.class);
}
*/
}
需要将security的配置类加到spring容器中,不然会抛出找不到springSecurityFilterChain这个bean的定义异常
编写springSecurity的过滤器
@Component
@Slf4j
public class JwtCheckFilter extends OncePerRequestFilter {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private RedisTemplate stringRedisTemplate;
@Autowired
private ObjectMapper objectMapper;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 从请求中获取URI
String requestURI = request.getRequestURI();
String[] path = {"/files/pictures","/start/doLogin","/code/image","/menu/myMenu","/start/register",
"/users/auditUser/info","/code/check","/collage/info","/activity/entry","/inform/all","/inform/getOne",
"/inform/type"};
for(String url : path){
if(requestURI.contains(url)){
doFilter(request,response,filterChain);
return; // 结束当前过滤器处理
}
}
// 获取请求头中的Authorization信息
String authorization = request.getHeader("Authorization");
// 如果Authorization为空,则记录警告信息,并重定向到登录页面
if(StringUtils.isEmpty(authorization)){
log.warn("没有Authorization,返回登录界面,去登录");
HttpResult httpResult = HttpResult.builder()
.code(403)
.msg("没有Authorization,返回登录界面,去登录")
.build();
printToken(request,response, httpResult);
return; // 结束当前过滤器处理
}
// 移除"bearer ",获取纯净的JWT字符串
String jwtToken = authorization.replace("bearer ", "");
// 如果jwt为空,则重定向到登录页面
if(StringUtils.containsWhitespace(jwtToken)){
log.warn("jwt为空,去登录");
HttpResult httpResult = HttpResult.builder()
.code(403)
.msg("jwt为空,去登录")
.build();
printToken(request,response, httpResult);
return; // 结束当前过滤器处理
}
// 验证JWT是否有效
boolean verifyResult = jwtUtils.verifyToken(jwtToken);
// 如果JWT验证失败,记录警告信息,并重定向到登录页面
if(!verifyResult){
log.warn("jwt非法");
HttpResult httpResult = HttpResult.builder()
.code(403)
.msg("jwt非法!!!!")
.build();
printToken(request,response, httpResult);
return; // 结束当前过滤器处理
}
// 检查Redis中是否存在该JWT的token
String redisToken = (String) stringRedisTemplate.opsForValue().get("logintoken" + jwtToken);
// 如果不存在Redis中的token,表示用户可能已经退出登录,需要重新登录
if(StringUtils.isEmpty(redisToken)){
log.warn("已经退出登录,无token,需要重新登陆");
HttpResult httpResult = HttpResult.builder()
.code(403)
.msg("您已经退出,请重新登录!!!!")
.build();
printToken(request,response, httpResult);
return; // 结束当前过滤器处理
}
// 从JWT中获取用户信息
String userInfo = jwtUtils.getUserInfoFromToken(jwtToken);
// 从JWT中获取用户权限列表
List userAuthList = jwtUtils.getUserAuthFromToken(jwtToken);
// 将用户信息转换为SysUser对象
SysUser sysUser = objectMapper.readValue(userInfo, SysUser.class);
// 将权限列表转换为授权对象列表
List authorityList = userAuthList.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
// 创建一个认证令牌,用于后续的安全框架认证
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(sysUser, null, authorityList);
// 在SecurityContext中设置认证信息,这样过滤器链中的其他过滤器就可以访问这些信息了
SecurityContextHolder.getContext().setAuthentication(token);
// 完成过滤器自己的处理逻辑后,调用chain.doFilter(request, response)继续过滤器链
doFilter(request,response,filterChain);
}
private void printToken(HttpServletRequest request, HttpServletResponse response, HttpResult httpResult) throws IOException {
String strResponse = objectMapper.writeValueAsString(httpResult);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println(strResponse);
writer.flush();
}
}
跨域过滤器
// 跨域过滤器
@Component
public class CrosFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String curOrigin = request.getHeader("Origin");
// 设置响应头
//该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接收任意域名的请求。
response.setHeader("Access-Control-Allow-Origin", "*");
//该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
//预检间隔时间
response.setHeader("Access-Control-Max-Age", "3600");
//该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段
response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-CAF-Authorization-Token,sessionToken,token,customercoderoute,authorization,conntectionid,Cookie,request-ajax");
//Access-Control-Allow-Credentials:该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。
// 默认情况下,Cookie不包括在CORS请求之中,设为True,
// 即表示服务器明确许可,Cookie可以包含在请求中,一起发送给服务器。
// 这个值也只能设为True,如果服务器不要浏览器发送Cookie,删除即可
// Access-Control-Allow-Credentials为True的时候,Access-Control-Allow-Origin一定不能设置为“*”,否则报错
response.setHeader("Access-Control-Allow-Credentials","true");
// 浏览器默认会发起异常 OPTIONS 的请求方式 这个时候我们通过过滤器直接拦截返回200后就可以解决跨越问题
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void destroy() {
}
}
剩下就是编写增删改查,以上是前后端分离架构ssm项目编写