石狮网站建设价格/优化搜索引擎的方法
一、导入依赖(pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.wgz</groupId><artifactId>wgz-security-auth</artifactId><version>0.0.1-SNAPSHOT</version><name>wgz-security-auth</name><description>账号统一认证</description><properties><java.version>8</java.version><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><spring-boot.version>2.7.12</spring-boot.version><spring-cloud.version>2021.0.7</spring-cloud.version><spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><lombok.version>1.18.24</lombok.version><mysql.version>8.0.31</mysql.version><mybatis-plus.version>3.5.3.1</mybatis-plus.version><pagehelper.version>1.4.6</pagehelper.version><knife4j.version>4.1.0</knife4j.version><fastjson.version>2.0.23</fastjson.version><redisson-spring.version>3.13.6</redisson-spring.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!--mybatis分页插件--><dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId></dependency><!--参数验证依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><!--thymeleaf模板引擎--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity5</artifactId></dependency><!-- 热部署插件--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><!--使用redisson作为分布式锁--><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId></dependency><!-- mysql数据库驱动 --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>${mysql.version}</version></dependency><!--mybati-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatis-plus.version}</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>${mybatis-plus.version}</version></dependency><!--引入spring boot 集成的PageHelper分页插件--><dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>${pagehelper.version}</version></dependency><!--lombok依赖--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--添加fastjson依赖--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${fastjson.version}</version></dependency><!-- 文档工具 knife4j-openapi3--><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-spring-boot-starter</artifactId><version>${knife4j.version}</version></dependency><!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency></dependencies><dependencyManagement><dependencies><!-- spring boot 依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency><!-- spring cloud 依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><!-- spring cloud alibaba 依赖 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency><!--使用redisson作为分布式锁--><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>${redisson-spring.version}</version></dependency><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-data-27</artifactId><version>${redisson-spring.version}</version></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.9.0</version><configuration><source>${java.version}</source><target>${java.version}</target><encoding>${project.build.sourceEncoding}</encoding><annotationProcessorPaths><path><groupId>com.github.therapi</groupId><artifactId>therapi-runtime-javadoc-scribe</artifactId><version>0.15.0</version></path><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></path><path><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><version>${spring-boot.version}</version></path></annotationProcessorPaths></configuration></plugin></plugins><resources><resource><directory>src/main/resources</directory><!-- 关闭过滤 --><filtering>false</filtering></resource><resource><directory>src/main/resources</directory><!-- 引入所有 匹配文件进行过滤 --><includes><include>application*</include><include>bootstrap*</include><include>banner*</include></includes><!-- 启用过滤 即该资源中的变量将会被过滤器中的值替换 --><filtering>true</filtering></resource></resources></build><profiles><profile><id>dev</id><properties><!-- 环境标识,需要与配置文件的名称相对应 --><profiles.active>dev</profiles.active><logging.level>info</logging.level></properties></profile><profile><id>test</id><properties><!-- 环境标识,需要与配置文件的名称相对应 --><profiles.active>test</profiles.active><logging.level>debug</logging.level></properties></profile><profile><id>uat</id><properties><profiles.active>uat</profiles.active><logging.level>debug</logging.level></properties></profile><profile><id>prod</id><properties><profiles.active>prod</profiles.active><logging.level>warn</logging.level></properties></profile></profiles>
</project>
二、application配置
1、application.yml
spring:profiles:active: @profiles.active@main:allow-circular-references: trueweb:resources:static-locations: classpath:/static/thymeleaf:cache: falseprefix: classpath:/templates/security:user:name: jingpassword: 1234
2、application-dev.yml
server:port: 3001spring:#数据源datasource:mysql:driverClassName: com.mysql.cj.jdbc.DriverjdbcUrl: jdbc:mysql://127.0.0.1:3306/wgz-security-auth?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&autoReconnect=true&allowMultiQueries=truetype: com.zaxxer.hikari.HikariDataSourceusername: rootpassword: 123456initialSize: 10maxActive: 20minIdle: 1maximumPoolSize: 20autoCommit: truepoolName: HikariPool_mysqlmaxLifetime: 600000connectionTestQuery: SELECT 1# redisredis:database: 2host: 127.0.0.1port: 6379username:password: 123456timeout: -1lettuce:pool:max-active: 8 # 连接池最大连接数(使用负值表示没有限制) 默认 8max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1max-idle: 8 # 连接池中的最大空闲连接 默认 8min-idle: 0 # 连接池中的最小空闲连接 默认 0#knife4j文档配置
knife4j:enable: true #是否开启Swaggerproduction: false #当前环境是否为生产环境basic:enable: true #进入界面是否需要账号密码username: adminpassword: admin123131
三、security config配置
import com.wgz.auth.Filter.TokenAuthenticationFilter;
import com.wgz.auth.Filter.TokenLoginFilter;
import com.wgz.auth.service.impl.UserDetailServiceImpl;
import com.wgz.auth.util.CustomPwdEncoderUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;/*** spring security 配置* 1.AuthenticationManager 认证管理器bean对象* 2.configure HttpSecurity 需要开启和绕过防护的接口 + 过滤器注册 + session管理* 3.configure AuthenticationManagerBuilder 认证管理器使用的资源* 4.configure WebSecurity 不需要拦截的路径*/
@Slf4j
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {/*** 自定义查询用户信息类(默认在providerService查询)*/private final UserDetailServiceImpl userService;/*** 自定义加密工具类*/private final CustomPwdEncoderUtil customPwdEncoder;/*** redis存储对象*/private final RedisTemplate<String, String> redisTemplate;/*** 配置认证管理器bean对象*/@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}/*** 防护配置:配置需要开启和绕过防护的接口 + 过滤器注册 + session管理*/@Overrideprotected void configure(HttpSecurity http) throws Exception {// //自定义登陆页面// http// .formLogin()// //登陆页面设置(页面访问的地方)// .loginPage("/login")// //登陆访问路径(表单访问的地方[post])// .loginProcessingUrl("/login/doLogin")// //登陆成功后跳转路径(页面)// .defaultSuccessUrl("/login/success").permitAll()// .and()// .authorizeRequests()// //设置不需要认证的访问路径,可以直接访问// .antMatchers("/", "/login/doLogin").permitAll();//配置认证信息:配置绕过的接口和注册过滤器(1.登录验证过滤器 + 2.token认证过滤器)http//关闭csrf.csrf().disable()//开启跨域以便前端调用接口.cors().and().authorizeRequests()//配置不需要认证的接口 如:登录接口.antMatchers("/login/doLogin").anonymous()//其他所有接口需要认证才能访问.anyRequest().authenticated().and()//添加token认证过滤器 方式一// .addFilterBefore(new TokenAuthenticationFilter(redisTemplate), UsernamePasswordAuthenticationFilter.class)//添加登录过滤器.addFilter(new TokenLoginFilter(authenticationManagerBean(), redisTemplate))//添加token认证过滤器 方式二.addFilterAfter(new TokenAuthenticationFilter(redisTemplate), UsernamePasswordAuthenticationFilter.class);//配置session信息:禁用http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);}/*** 配置认证管理器使用的资源:* 1.查询用户信息的类* 2.自定义加密器*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userService).passwordEncoder(customPwdEncoder);}/*** 配置不需要拦截的路径* 配置后将不再进入过滤器进行校验,直接请求到对应的资源地址* 如:包含/login/doLogin ,* 那在请求这个接口时,直接到达该接口地址,* 不再进入security过滤器*/@Overridepublic void configure(WebSecurity web) {web.ignoring().antMatchers("/favicon.ico","/swagger-resources/**","/webjars/**","/v2/**","/v3/**",// "/login/**", //放开该地址后,请求将不再进入过滤器校验与认证,如:不使用过滤器的方式校验登录"/swagger-ui.html","/doc.html");}
}
四、redis配置
1、redis config
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** redis配置*/
@EnableCaching
@Configuration(proxyBeanMethods = false)
@RequiredArgsConstructor
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedisConfig {private final RedisConnectionFactory factory;@Beanpublic RedisTemplate<String, Object> redisTemplate() {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());redisTemplate.setConnectionFactory(factory);return redisTemplate;}@Beanpublic HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForHash();}@Beanpublic ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {return redisTemplate.opsForValue();}@Beanpublic ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForList();}@Beanpublic SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForSet();}@Beanpublic ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForZSet();}
}
2、redisson config
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;/*** redisson配置*/
@Configuration
public class RedissonConfig {@Value("${spring.redis.host:127.0.0.1}")private String host;@Value("${spring.redis.port:6379}")private String port;@Value("${spring.redis.password}")private String password;@Value("${spring.redis.username:''}")private String username;@Value("${spring.redis.database:1}")private Integer database;@Beanpublic RedissonClient singleRedisClient() {Config config = new Config();SingleServerConfig singleServerConfig = config.useSingleServer();if (StringUtils.hasLength(password)) {singleServerConfig.setPassword(password);}if (StringUtils.hasLength(username)) {singleServerConfig.setUsername(username);}String url = "redis://" + host + ":" + port;singleServerConfig.setAddress(url);singleServerConfig.setDatabase(database);return Redisson.create(config);}
}
五、mysql config配置
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.support.JdbcTransactionManager;
import org.springframework.transaction.TransactionManager;import javax.sql.DataSource;
import java.util.*;/*** 数据源配置*/
@Configuration
//这里设置扫描dao接口了,启动类与dao接口就不用在配置mapper扫描注解
@MapperScan(basePackages = "com.wgz.auth.mapper", sqlSessionFactoryRef = "mySqlSessionFactory")
public class HikariMySqlConfig {/*** @ConfigurationProperties 读取yml中的配置参数映射成为一个对象*/@Bean(name = "mySqlDataSource")@ConfigurationProperties(prefix = "spring.datasource.mysql")public HikariDataSource mySqlDateSource() {return new HikariDataSource();}@Bean(name = "mySqlSessionFactory")public SqlSessionFactory mysqlSqlSessionFactory(@Qualifier("mySqlDataSource") DataSource datasource) throws Exception {MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();bean.setDataSource(datasource);//-----多路径是扫描//配置xml扫描路径List<String> resourcePatterns = new ArrayList<>();resourcePatterns.add("classpath*:mapper/*.xml");resourcePatterns.add("classpath*:mapper/*/*.xml");Set<Resource> resources = new LinkedHashSet<>(resourcePatterns.size());for (String resourcePattern : resourcePatterns) {Resource[] resource = new PathMatchingResourcePatternResolver().getResources(resourcePattern);resources.addAll(Arrays.asList(resource));}Resource[] resourcesArr = new Resource[resources.size()];//mybatis扫描xml所在位置bean.setMapperLocations(resources.toArray(resourcesArr));//-----单路径是扫描//mybatis扫描xml所在位置//bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml"));return bean.getObject();}@Bean("mySqlSessionTemplate")public SqlSessionTemplate mysqlSqlSessionTemplate(@Qualifier("mySqlSessionFactory") SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate(sqlSessionFactory);}//不配值这个bean,@Transaction注解可能失效//使用时 @Transactional(value = "mySqlTransactionManager",rollbackFor = {Exception.class, RuntimeException.class})@Bean("mySqlTransactionManager")public TransactionManager mysqlTransactionManager() {return new JdbcTransactionManager(mySqlDateSource());}
}
六、Open Api文档配置
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** OpenApi 配置*/
@Configuration
public class OpenApiConfig {@Beanpublic OpenAPI customOpenAPI() {return new OpenAPI().info(new Info().title("认证中心").contact(new Contact().name("XX.XX").email("xxx@zz.com")).version("1.0").description("认证中心接口文档").license(new License().name("Apache 2.0")));}
}
七、AES加密工具
import org.springframework.util.DigestUtils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;import static com.wgz.auth.constant.Common.AES_SECRET_KEY;/*** AES 加密工具类*/
public class AesUtil {// 加密算法RSApublic static final String KEY_ALGORITHM = "AES";//编码方式public static final String CODE_TYPE = "UTF-8";//填充类型 AES/ECB/PKCS5Padding AES/ECB/ISO10126Paddingpublic static final String AES_TYPE = "AES/ECB/PKCS5Padding";/*** 自定义内容加盐,生成AES秘钥*/public static String generateAESKey() {return DigestUtils.md5DigestAsHex(getSalt(6));}/*** 随机生成加盐类*/public static byte[] getSalt(int n) {char[] chars = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +"1234567890!@#$%^&*()_+").toCharArray();StringBuilder stringBuilder = new StringBuilder();SecureRandom random = new SecureRandom();for (int i = 0; i < n; i++) {stringBuilder.append(chars[random.nextInt(chars.length)]);}return stringBuilder.toString().getBytes();}/*** 加密** @param clearText 明文* @param aesKey AES秘钥* @return 加密串*/public static String encryptAes(String clearText, String aesKey) {try {SecretKeySpec key = new SecretKeySpec(aesKey.getBytes(), KEY_ALGORITHM);Cipher cipher = Cipher.getInstance(AES_TYPE);cipher.init(Cipher.ENCRYPT_MODE, key);byte[] encryptedData = cipher.doFinal(clearText.getBytes(CODE_TYPE));return new BASE64Encoder().encode(encryptedData);} catch (Exception e) {throw new RuntimeException("加密失败", e);}}/*** 解密** @param encryptText 密文* @param aesKey AES秘钥* @return 解密串*/public static String decryptAes(String encryptText, String aesKey) {try {byte[] byteMi = new BASE64Decoder().decodeBuffer(encryptText);SecretKeySpec key = new SecretKeySpec(aesKey.getBytes(), KEY_ALGORITHM);Cipher cipher = Cipher.getInstance(AES_TYPE);cipher.init(Cipher.DECRYPT_MODE, key);byte[] decryptedData = cipher.doFinal(byteMi);return new String(decryptedData, CODE_TYPE);} catch (Exception e) {throw new RuntimeException("解密失败", e);}}public static void main(String[] args) {//String aesKey = generateAESKey();String json = "admin123131";//加密System.out.println("字符串:" + json);String encrypt = encryptAes(json, AES_SECRET_KEY);System.out.println("加密后字符串:" + encrypt);//私钥解密System.out.println("解密后字符串:" + decryptAes(encrypt, AES_SECRET_KEY));}
}
八、自定义密码处理组件
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;import static com.wgz.auth.constant.Common.AES_SECRET_KEY;//一、自定义密码处理组件
@Component
public class CustomPwdEncoderUtil implements PasswordEncoder {@Overridepublic String encode(CharSequence rawPassword) {//进行MD5加密//MD5.encrypt(rawPassword.toString());return AesUtil.encryptAes(String.valueOf(rawPassword), AES_SECRET_KEY);}@Overridepublic boolean matches(CharSequence rawPassword, String encodePassword) {//判断是否相等//return encodePassword.equals(MD5.encrypt(rawPassword.toString()));return AesUtil.encryptAes(String.valueOf(rawPassword), AES_SECRET_KEY).equals(encodePassword);}
}
九、Response工具
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wgz.auth.constant.Result;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class ResponseUtil {public static void out(HttpServletResponse response, Result r) {ObjectMapper mapper = new ObjectMapper();response.setStatus(HttpStatus.OK.value());response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);try {mapper.writeValue(response.getWriter(), r);} catch (IOException e) {e.printStackTrace();}}
}
十、common constant 常量
import java.time.Duration;public interface Common {/*** spring security 继承过滤器(UsernamePasswordAuthenticationFilter)验证方式:TokenLoginFilter*/String TOKEN_PREF_DEFAULT = "AUTH:TOKEN:DEFAULT:";/*** spring security 自定义接口验证方式:* com.wgz.auth.controller.LoginController#doLogin(com.wgz.auth.entity.UserLoginDto)*/String TOKEN_PREF_INTERFACE = "AUTH:TOKEN:INTERFACE:";/*** 登录过期时间*/Duration REDIS_TIME_OUT = Duration.ofHours(3);/*** AES密钥*/String AES_SECRET_KEY = "a57e3db23f9c89e606abe9b9b5fc7f30";
}
十一、自定义UserDetail类
import com.wgz.auth.entity.user.UserVo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;import java.util.Collection;/*** 自定义用户类(继承Security里的User类)*/
@Getter
@Setter
@ToString
@Schema(description = "自定义用户类: 继承Security里的User类")
public class CustomUser extends User {/*** 我们自己的用户实体对象,要调取用户信息时直接获取这个实体对象*/private UserVo userVo;public CustomUser(UserVo userVo, Collection<? extends GrantedAuthority> authorities) {//调用父类构造器初始化信息super(userVo.getUserName(), userVo.getPassword(), authorities);this.userVo = userVo;}
}
十二、登录过滤器配置
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wgz.auth.entity.user.UserLoginDto;
import com.wgz.auth.constant.ResponseEnum;
import com.wgz.auth.constant.Result;
import com.wgz.auth.handler.CustomException;
import com.wgz.auth.util.ResponseUtil;
import lombok.SneakyThrows;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;/*** token 登录过滤器:* 1.【只对】配置的登录路径进行拦截,* 2.拦截后再对提交的登录信息进行认证* 3.认证成功后颁发token,认证失败提示*/
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {private RedisTemplate<String, String> redisTemplate;/*** 初始化:资源准备*/public TokenLoginFilter(AuthenticationManager authenticationManager, RedisTemplate<String, String> redisTemplate) {this.redisTemplate = redisTemplate;//设置/初始化 认证管理器this.setAuthenticationManager(authenticationManager);//取消 只针对post请求this.setPostOnly(false);//指定登录接口及提交方式,可以指定任意路径this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login/doLogin","POST"));}/*** 拦截提交的登录信息并进行认证*/@SneakyThrows@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {// //1.request param body为form-data的方式(如:一般未处理的表单提交方式)// UserLoginDto loginDto = new UserLoginDto();// loginDto.setUsername(request.getParameter("username"));// loginDto.setPassword(request.getParameter("password"));//2.request param body为json的方式UserLoginDto loginDto = new ObjectMapper().readValue(request.getInputStream(), UserLoginDto.class);//创建UsernamePasswordAuthenticationToken对象,封装用户名和密码,得到认证对象Authentication authenticationToken = new UsernamePasswordAuthenticationToken(loginDto.getUsername(), loginDto.getPassword());//返回目标认证对象return this.getAuthenticationManager().authenticate(authenticationToken);}/*** 认证成功后的方法*/@Overrideprotected void successfulAuthentication(HttpServletRequest request,HttpServletResponse response,FilterChain chain,Authentication auth) {if (auth.getPrincipal() == null) {throw new CustomException(ResponseEnum.PARAM_ERROR.getCode(),"用户名或密码错误!");}String token = UUID.randomUUID().toString().replaceAll("-","");Map<String, String> result = new HashMap<>();result.put("token", token);redisTemplate.opsForValue().set(TOKEN_PREF_DEFAULT + token, JSONObject.toJSONString(auth.getPrincipal()), REDIS_TIME_OUT);ResponseUtil.out(response, Result.data(result));// CustomUser user = (CustomUser) auth.getPrincipal();// //2.生成token// String token = JwtHelper.createToken(null, user.getUsername());// //3.返回(通过响应工具)// Map<String, Object> map = new HashMap<>();// map.put("token", token);// request.getSession().setAttribute("token",token);// ResponseUtil.out(response, Result.data(result));}/*** 认证失败后的方法*/@Overrideprotected void unsuccessfulAuthentication(HttpServletRequest request,HttpServletResponse response,AuthenticationException e) {e.printStackTrace();ResponseUtil.out(response, new Result(ResponseEnum.PARAM_ERROR.getCode(),"用户名或密码错误!", null));}
}
十三、认证过滤器配置
import com.alibaba.fastjson.JSONObject;
import com.wgz.auth.constant.ResponseEnum;
import com.wgz.auth.constant.Result;
import com.wgz.auth.entity.user.UserVo;
import com.wgz.auth.util.ResponseUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;import static com.wgz.auth.constant.Common.TOKEN_PREF_DEFAULT;
import static com.wgz.auth.constant.Common.TOKEN_PREF_INTERFACE;/*** token认证过滤器:* 1.对没有开放拦截的接口进行token认证* 2.认证成功放行、认证失败拦截并提示* PS: 注册方式 addFilterBefore or addFilterAfter*/
public class TokenAuthenticationFilter extends OncePerRequestFilter {private RedisTemplate<String, String> redisTemplate;/*** 初始化:资源准备*/public TokenAuthenticationFilter(RedisTemplate<String, String> redisTemplate) {this.redisTemplate = redisTemplate;}/*** 拦截没有未开放的接口并认证token是否有效,* token有效放行、token无效拦截并提示*/@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain) throws ServletException, IOException {//1.获取认证信息UsernamePasswordAuthenticationToken authentication = getAuthentication(request, response);//2.将认证信息存储到SecurityContextHolder对象中SecurityContextHolder.getContext().setAuthentication(authentication);filterChain.doFilter(request, response);}/*** 获取用户信息,一级校验token* 一、TOKEN_PREF_DEFAULT:* 1.spring security 过滤器登录方式:在TokenLoginFilter(继承 UsernamePasswordAuthenticationFilter )的attemptAuthentication方法中验证登录信息、* 2.在TokenLoginFilter中指定登录接口* 二、TOKEN_PREF_INTERFACE* 1.spring security 自定义接口登录方式: 在自己的controller接口内验证登录信息、* 2.在SecurityConfig放开指定的登录接口*/private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request, HttpServletResponse response) {String token = request.getHeader("token");if (!StringUtils.hasLength(token)) {return null;}// //自定义的接口登录方式// String redisStr = redisTemplate.opsForValue().get(TOKEN_PREF_INTERFACE + token);//默认的继承过滤器方式String redisStr = redisTemplate.opsForValue().get(TOKEN_PREF_DEFAULT + token);if (!StringUtils.hasLength(redisStr)) {ResponseUtil.out(response, new Result(ResponseEnum.NOT_PERMISSION.getCode(),"无效的token,请重新登录!", null));return null;}UserVo user = JSONObject.parseObject(redisStr, UserVo.class);if (ObjectUtils.isEmpty(user)) {ResponseUtil.out(response, new Result(ResponseEnum.SERVICE_ERROR.getCode(),"认证信息转换失败,请联系客户或管理员!", null));return null;}//1.从redis中获取权限信息//2.将权限信息传给 UsernamePasswordAuthenticationToken//返回认证信息return new UsernamePasswordAuthenticationToken(user.getUserName(),null, null);}
}
十四、自定义UserDetailServiceImpl
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wgz.auth.entity.security.CustomUser;
import com.wgz.auth.entity.user.UserVo;
import com.wgz.auth.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;import java.util.List;@Service
@RequiredArgsConstructor
public class UserDetailServiceImpl implements UserDetailsService {private final UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {LambdaQueryWrapper<UserVo> lambdaQueryUser = new LambdaQueryWrapper<>();lambdaQueryUser.eq(UserVo: :getUserName, username);lambdaQueryUser.eq(UserVo: :getIsDelete,0);lambdaQueryUser.last("limit 1");UserVo userVo = userMapper.selectOne(lambdaQueryUser);//判断用户是否为空if (null == userVo) {throw new UsernameNotFoundException("用户不存在");}//判断用户状态是否可用if (userVo.getStatus() == 0) {throw new RuntimeException("用户已禁用...");}//手动设置权限,也可以通过数据库查询获取List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("addUser,findAll,ROLE_admin,ROLE_user");return new CustomUser(userVo, auths);}
}
相关文章:

Spring Boot 集成 spring security 01
一、导入依赖(pom.xml) <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation&qu…...

C 编程中使用字符串
理解字符串: C 中的字符串是使用字符数组来操作的。数组中的每个字符对应字符串的一个元素,字符串的结尾由空字符(\0)标记。这个空字符至关重要,因为它表示字符串的结尾,并允许函数确定字符串在内存中的结…...

【GD32307E-START】04 使用TinyMaix进行手写数字识别
【GD32307E-START】04 使用TinyMaix进行手写数字识别 参考博客 【GD32F427开发板试用】使用TinyMaix进行手写数字识别 https://blog.csdn.net/weixin_47569031/article/details/129009839 软硬件平台 GD32F307E-START Board开发板GCC Makefile TinyMaix简介 TinyMaix是国…...

qt-C++笔记之识别点击鼠标右键、点击位置以及Qt坐标系详解
qt-C笔记之识别点击鼠标右键、点击位置以及Qt坐标系详解 code review! 文章目录 qt-C笔记之识别点击鼠标右键、点击位置以及Qt坐标系详解1.示例运行2.event->pos();详解3.event->pos()的坐标系原点4.Qt中的坐标系详解5.QMainWindow::mousePressEvent(event);详解 1.示例…...

小程序开发平台源码系统:搭建新的商业体系 附带完整的搭建教程
小程序开发平台源码系统是在移动互联网快速发展的背景下诞生的。随着微信小程序的普及,越来越多的人开始关注小程序的开发与运营。然而,对于很多初学者和小型企业来说,开发一个小程序需要专业的技术知识和大量的时间投入,这无疑是…...

css3新增的伪类有哪些?
CSS3新增的伪类有: :first-of-type,选择属于其父元素的特定类型的第一个子元素。:last-of-type,选择属于其父元素的特定类型的最后一个子元素。:only-of-type,选择属于其父元素的特定类型的唯一子元素。:only-child,选…...

开源软件license介绍与检测
开源License介绍 通俗来讲,开源许可证就是一种允许软件使用者在一定条件内按照需要自由使用和修改软件及其源代码的的法律条款。借此条款,软件作者可以将这些权利许可给使用者,并告知使用限制。这些许可条款可以由个人、商业公司或非赢利组织…...

【LeeCode】142.环形链表II
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数…...

nodejs微信小程序+python+PHP健身房信息管理系统的设计与实现-计算机毕业设计推荐
目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性:…...

springboot集成springsecurity
转载自:www.javaman.cn 1、整合springsecurity 添加pom.xml <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId> </dependency>2、springsecurity认证授权流程…...

脏读、不可重复读、幻读
一、脏读 A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。就好像原本的数据比较干净、纯粹,此时由于B事务更改了它,这个数据变得不再纯粹。这个时候A事务立即读取了这个脏…...

思维模型 反馈效应
本系列文章 主要是 分享 思维模型,涉及各个领域,重在提升认知。反馈促进改进。 1 反馈效应的应用 1.1 反馈效应在营销中的应用 1 “可口可乐与百事可乐之战” 在 20 世纪 80 年代,可口可乐公司是全球最大的饮料公司之一,其市场…...

【PyTorch】线性回归
文章目录 1. 模型与代码实现2. Q&A 1. 模型与代码实现 模型 y ^ w 1 x 1 . . . w d x d b w ⊤ x b . \hat{y} w_1 x_1 ... w_d x_d b \mathbf{w}^\top \mathbf{x} b. y^w1x1...wdxdbw⊤xb. 代码实现 import torch from torch import nn from to…...

硝烟弥漫的科技战场——GPT之战
没想到2023年的双11之后,还能看到如此多的科技圈大佬针对GPT提出火药味十足的讨论和极具戏剧性的表演。 历史回顾: 11月6日,OpenAI发布会:GPT-4 Turbo模型、GPT应用商店、开源Whisper-large-v3等;11月17日࿰…...

re:Invent 构建未来:云计算生成式 AI 诞生科技新局面
文章目录 前言什么是云计算云计算类型亚马逊云科技云计算最多的功能最大的客户和合作伙伴社区最安全最快的创新速度最成熟的运营专业能力 什么是生成式 AI如何使用生成式 AI后记 前言 在科技发展的滚滚浪潮中,我们见证了云计算的崛起和生成式 AI 的突破,…...

oneApi实现并⾏排序算法
零、OneApi简介 oneAPI是由英特尔推出的一个开放、统一的编程模型和工具集合,旨在简化跨不同硬件架构的并行计算。oneAPI的目标是提供一个统一的编程模型,使开发人员能够使用相同的代码在不同类型的硬件上进行并行计算,包括CPU、GPU、FPGA和…...

语音芯片的BUSY状态指示功能特征:提升用户体验与系统稳定性的关键
在电子产品的音频系统中,语音芯片扮演着至关重要的角色。为了保证音频的流畅播放和功能的正常运行,语音芯片的各种状态指示功能变得尤为重要。其中,BUSY状态指示功能是语音芯片中的一项关键特征,它对于提升用户体验和系统稳定性具…...

Leetcode2661. 找出叠涂元素
Every day a Leetcode 题目来源:2661. 找出叠涂元素 解法1:哈希 题目很绕,理解题意后就很简单。 由于矩阵 mat 中每一个元素都不同,并且都在数组 arr 中,所以首先我们用一个哈希表 hash 来存储 mat 中每一个元素的…...

免费最新6款热门SEO优化排名工具
网站的存在感对于业务和品牌的成功至关重要。在众多网站推广方法中,搜索引擎优化(SEO)是提高网站可见性的关键。而SEO的核心之一就是关键词排名。为了更好地帮助您优化网站。 SEO关键词排名工具 在如今信息过载的互联网时代,用户…...

绝地求生在steam叫什么?
绝地求生在Steam的全名是《PlayerUnknowns Battlegrounds》,简称为PUBG。作为一款风靡全球的多人在线游戏,PUBG于2017年3月23日正式上线Steam平台,并迅速成为一部热门游戏。 PUBG以生存竞技为核心玩法,玩家将被投放到一个辽阔的荒…...

Elasticsearch:什么是大语言模型(LLM)?
大语言模型定义 大语言模型 (LLM) 是一种深度学习算法,可以执行各种自然语言处理 (natural language processing - NLP) 任务。 大型语言模型使用 Transformer 模型,并使用大量数据集进行训练 —— 因此规模很大。 这使他们能够识别、翻译、预测或生成文…...

Kubernetes1.27容器化部署Prometheus
Kubernetes1.27容器化部署Prometheus GitHub链接根据自己的k8s版本选择对应的版本修改镜像地址部署命令对Etcd集群进行监控(云原生监控)创建Etcd Service创建Etcd证书的Secret创建Etcd ServiceMonitorgrafana导入模板成功截图 对MySQL进行监控࿰…...

fasterxml 注解组装实体
使用 FasterXML Jackson 的注解 JsonTypeInfo 和 JsonSubTypes 可以实现多态类型的处理。在你的 User 类上,你可以添加这些注解来指示 Jackson 如何处理多态类型。 以下是使用 JsonTypeInfo 和 JsonSubTypes 注解的 User 类的修改: import com.fasterx…...

自写一个函数将js对象转为Ts的Interface接口
如今的前端开发typescript 已经成为一项必不可以少的技能了,但是频繁的定义Interface接口会给我带来许多工作量,我想了想如何来减少这些非必要且费时的工作量呢,于是决定写一个函数,将对象放进它自动帮我们转换成Interface接口&am…...

【数据结构】拆分详解 - 二叉树的链式存储结构
文章目录 一、前置说明二、二叉树的遍历 1. 前序、中序以及后序遍历 1.1 前序遍历 1.2 中序遍历 1.3 后序遍历 2. 层序遍历 三、常见接口实现 0. 递归中的分治思想 1. 查找与节点个数 1.1 节点个数 1.2 叶子节点个数 1.3 第k层节…...

Laravel修改默认的auth模块为md5(password+salt)验证
首先声明:这里只是作为一个记录,实行拿来主义,懒得去记录那些分析源码的过程,不喜勿喷,可直接划走。 第一步:创建文件夹:app/Helpers/Hasher; 第二步:创建文件: app/Help…...

OpenStack-train版安装之安装Keystone(认证服务)、Glance(镜像服务)、Placement
安装Keystone(认证服务)、Glance(镜像服务)、Placement 安装Keystone(认证服务)安装Glance(镜像服务)安装Placement 安装Keystone(认证服务) 数据库创建、创…...

【九日集训】第九天:简单递归
递归就是自己调用自己,例如斐波那契数列就是可以用简单递归来实现。 第一题 172. 阶乘后的零 https://leetcode.cn/problems/factorial-trailing-zeroes/description/ 这一题纯粹考数学推理能力,我这种菜鸡看了好久都没有懂。 大概是这样的思路&#x…...

Prime 1.0
信息收集 存活主机探测 arp-scan -l 或者利用nmap nmap -sT --min-rate 10000 192.168.217.133 -oA ./hosts 可以看到存活主机IP地址为:192.168.217.134 端口探测 nmap -sT -p- 192.168.217.134 -oA ./ports UDP端口探测 详细服务等信息探测 开放端口22&#x…...

Java 如何正确比较两个浮点数
看下面这段代码,将 d1 和 d2 两个浮点数进行比较,输出的结果会是什么? double d1 .1 * 3; double d2 .3; System.out.println(d1 d2);按照正常逻辑来看,d1 经过计算之后的结果应该是 0.3,最后打印的结果应该是 tru…...