spring+springMVC+mybatis+springSecurity(全配置类)

发布时间:2024年01月17日

spring+springMVC+mybatis+springSecurity(全配置类)

首先创建一个maven

导入依赖坐标

  <dependencies>
    <!--mybatis分页pageHelper-->
    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>5.2.0</version>
    </dependency>
    <!-- 添加jwt的依赖 -->
    <dependency>
      <groupId>com.auth0</groupId>
      <artifactId>java-jwt</artifactId>
      <version>3.11.0</version>
    </dependency>
    <!--引入spring-security-->
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-config</artifactId>
      <version>5.7.3</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-web</artifactId>
      <version>5.7.3</version>
    </dependency>
    <!--引入hutool-->
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-all</artifactId>
      <version>5.8.16</version>
    </dependency>
    <!--redis  客户端 jar 包依赖、Spring API jar 包依赖、连接池 jar 包依赖-->
    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>3.3.0</version>
    </dependency>

    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-redis</artifactId>
      <version>2.3.0.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-pool2</artifactId>
      <version>2.8.0</version>
    </dependency>

    <!--mybatis-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.0</version>
    </dependency>
    <!--json-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.4</version>
    </dependency>
    <!--springTest-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

    <!--slf4j core 使用slf4j必須添加-->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.27</version>
    </dependency>
    <!--slf4j 自带的简单日志实现 -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
      <version>1.7.27</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.24</version>
      <scope>provided</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>

    <!--SpringMVC-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.18</version>
    </dependency>
    <!--spring-jdbc-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.3.30</version>
    </dependency>
    <!--druid数据源-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.16</version>
    </dependency>
    <!--数据库连接-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.33</version>
    </dependency>
    <!--spring事务管理-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.3.30</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.3.30</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.14.1</version>
    </dependency>
    <!--poi的excel-->
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi</artifactId>
      <version>4.1.2</version>
    </dependency>

    <!--文件解析-->
    <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.3</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.4</version>
    </dependency>
    <!--Gson-->
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.8.9</version>
    </dependency>
  </dependencies>

编写配置类,首先配置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<String,String> 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<String,String> redisTemplate = new RedisTemplate<String,String>();
        redisTemplate.setConnectionFactory(connectionFactory);


        // 设置自定 Redis 序列化器
        RedisSerializer<Object> jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
        RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer();
        // 默认使用 StringRedisSerializer 作为 K,V 序列化存储
        redisTemplate.setDefaultSerializer(stringRedisSerializer);
        // 对于普通类型 K 使用 StringRedisSerializer 序列化存储
        redisTemplate.setKeySerializer(stringRedisSerializer);
        // 对于普通类型 V 使用 JdkSerializationRedisSerializer 序列化存储(不推荐存储占用空间大)
        redisTemplate.setValueSerializer(jdkSerializationRedisSerializer);
        // 对于 Hash 类型 K 使用 StringRedisSerializer 序列化存储
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        // 对于 Hash 类型 V 使用 JdkSerializationRedisSerializer 序列化存储(不推荐存储占用空间大)
        redisTemplate.setHashValueSerializer(jdkSerializationRedisSerializer);
        return redisTemplate;
    }
}

配置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<String> userAuthList = jwtUtils.getUserAuthFromToken(jwtToken);

        // 将用户信息转换为SysUser对象
        SysUser sysUser = objectMapper.readValue(userInfo, SysUser.class);

        // 将权限列表转换为授权对象列表
        List<SimpleGrantedAuthority> 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项目编写

文章来源:https://blog.csdn.net/KYGALYX/article/details/135580589
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。