org.springframework.boot
spring-boot-starter-data-redis
org.springframework.session
spring-session-data-redis
org.springframework.boot
spring-boot-starter-security
io.jsonwebtoken
jjwt-api
0.11.5
io.jsonwebtoken
jjwt-impl
0.11.5
runtime
io.jsonwebtoken
jjwt-jackson
0.11.5
runtime
com.feiniaojin
graceful-response
3.5.2-boot2
spring.application.name=test
server.port=8080
# DataSource Config
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
spring.main.allow-circular-references:true
# Redis config
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
spring.session.store-type=redis
server.servlet.session.timeout=1800
#graceful GracefulResponseException 可选看你自己需不需要
graceful-response.rqesponse-class-full-name
graceful-response.response-style=1
graceful-response.default-success-msg=success
graceful-response.default-success-code=200
graceful-response.default-error-msg=error
graceful-response.default-error-code=201
graceful-response.origin-exception-using-detail-message=true
print-exception-in-global-advice:false
import com.mybatisflex.core.query.QueryWrapper;
import com.自己的实体类.entity.User;
import com.自己的服务类.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static com.自己的实体类2.entity.table.MyUserDef.USER;
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private RedisService redisService;
@Autowired
private UserService userService; // 你的用户服务
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String token = getJwtFromRequest(request);
if (token != null && jwtUtils.validateToken(token)) {
String username = jwtUtils.getUsernameFromToken(token);
// 从数据库加载用户信息
QueryWrapper queryWrapper = QueryWrapper.create()
.select(USER.ALL_COLUMNS)
.from(USER)
.where(USER.NAME.eq(username));
User user = userService.getOne(queryWrapper);
if (user != null) {
// 创建认证信息,注意第三个参数需要提供用户权限
List authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ADMIN")); // 添加默认角色
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
user, null, authorities); // 这里传递权限列表
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
// 尝试从请求头获取
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
return header.substring(7);
}
// 尝试从Cookie获取
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("Authorization".equals(cookie.getName())) {
return cookie.getValue();
}
}
}
return null;
}
}
import io.jsonwebtoken.*;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtUtils {
private static final String SECRET_KEY = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 建议从配置文件读取
private static final long EXPIRATION_TIME = 86400000; // 24小时
// 生成JWT
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
// 验证JWT
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
return true;
} catch (MalformedJwtException | ExpiredJwtException |
UnsupportedJwtException | IllegalArgumentException e) {
return false;
}
}
// 从JWT中获取用户名
public String getUsernameFromToken(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY)
.parseClaimsJws(token).getBody().getSubject();
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class RedisService {
private static final String TOKEN_PREFIX = "token:";
private static final long EXPIRE_TIME = 86400; // 24小时
@Autowired
private StringRedisTemplate redisTemplate;
// 存储Token到Redis
public void saveToken(String username, String token) {
redisTemplate.opsForValue().set(TOKEN_PREFIX + username, token, EXPIRE_TIME, TimeUnit.SECONDS);
}
// 从Redis获取Token
public String getToken(String username) {
return redisTemplate.opsForValue().get(TOKEN_PREFIX + username);
}
// 删除Token
public void deleteToken(String username) {
redisTemplate.delete(TOKEN_PREFIX + username);
}
// 验证Token
public boolean validateToken(String username, String token) {
String storedToken = getToken(username);
return storedToken != null && storedToken.equals(token);
}
}
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.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.Collections;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and() // 启用CORS
.csrf().disable()
.authorizeRequests()
.antMatchers("/douyu_sanxia/login/toLogin").permitAll() // 允许登录接口匿名访问
.anyRequest().authenticated() // 其他接口需要认证
.and()
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOriginPattern("*"); // 允许所有域名进行跨域调用
config.addAllowedHeader("*"); // 允许任何请求头
config.addAllowedMethod("*"); // 允许任何方法(POST、GET等)
config.setAllowCredentials(true); // 允许携带凭证
// 注意:当allowCredentials为true时,allowedOriginPatterns不能使用通配符*
// 如果需要允许所有域名,建议改用以下方式:
config.setAllowedOriginPatterns(Collections.singletonList("*"));
config.setAllowedHeaders(Collections.singletonList("*"));
config.setAllowedMethods(Collections.singletonList("*"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config); // 对所有接口都有效
return new CorsFilter(source);
}
}
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 开启全局跨域配置文件
*/
@Configuration
public class GlobalCorsConfig implements WebMvcConfigurer {
static final String ORIGINS[] = new String[]{"GET", "POST", "PUT", "DELETE", "OPTIONS"};
@Override
public void addCorsMappings(CorsRegistry registry) {
// 所有的当前站点的请求地址,都支持跨域访问
registry.addMapping("/**")
// 所有的外部域都可跨域访问 如果是localhost则很难配置,因为在跨域请求的时候,外部域的解析可能是localhost、127.0.0.1、主机名
.allowedOriginPatterns("*")
// 是否支持跨域预约凭证
.allowCredentials(true)
// 当前站点支持的跨域请求类型是什么
.allowedMethods(ORIGINS)
// 超时时长设置为1小时 时间单位是秒
.maxAge(3600);
}
}
GracefulResponseException可选可不选,看你需要
import com.feiniaojin.gracefulresponse.GracefulResponseException;
import com.mybatisflex.core.query.QueryWrapper;
import com.自己的实体类.entity.User;
import com.自己的实体VO类.entity.vo.LoginUserVo;
import com.自己的服务类.service.UserService;
import com.test.utils.jwt.JwtUtils;
import com.test.utils.jwt.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import static com.自己的实体类2.entity.table.MyUserDef.USER;
/**
* 登录信息
*
*/
@RestController
@RequestMapping("/test/login")
public class LoginController {
@Autowired
private UserService userService;
@Autowired
private RedisService redisService;
/**
* 登录
*
* @param loginUserVo 登录的vo类
* @return
* @throws Exception
*/
@PostMapping("/toLogin")
public User toLogin(@RequestBody LoginUserVo loginUserVo, HttpServletResponse response) throws Exception {
QueryWrapper queryWrapper = QueryWrapper.create()
.select(USER.ALL_COLUMNS)
.from(USER)
.where(USER.NAME.eq(loginUserVo.getUsername()));
User user = userService.getOne(queryWrapper);
if (user == null) {
throw new GracefulResponseException("用户不存在");
}
if (!user.getPwd().equals(loginUserVo.getPassword())) {
throw new GracefulResponseException("密码错误");
}
// 生成JWT
String token = JwtUtils.generateToken(user.getName());
// 存入Redis
redisService.saveToken(user.getName(), token); // 添加这一行
// 创建Cookie存储Token
Cookie tokenCookie = new Cookie("Authorization", token);
tokenCookie.setHttpOnly(true); // 防止XSS攻击
tokenCookie.setSecure(false); // 开发环境使用HTTP时设置为false
tokenCookie.setPath("/"); // 整个应用都可以访问
tokenCookie.setMaxAge(30 * 60); // 设置有效期:30min * 60s = 1800s = 30分钟
response.addCookie(tokenCookie);
return user;
}
}