2 Commits

Author SHA1 Message Date
cxuan
b743d8039f 整合spring security(乱七八糟) 2023-07-21 17:21:35 +08:00
cxuan
f9e3d80dd1 security config 2023-07-14 19:56:14 +08:00
8 changed files with 354 additions and 0 deletions

24
pom.xml
View File

@@ -25,6 +25,10 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.mysql</groupId> <groupId>com.mysql</groupId>
@@ -41,6 +45,26 @@
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.3.Final</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -0,0 +1,56 @@
package com.wuyiqi.netstateproc.filter;
import com.wuyiqi.netstateproc.model.vo.UserInfoVo.UserBasicInfoVo;
import com.wuyiqi.netstateproc.security.token.JwtUser;
import com.wuyiqi.netstateproc.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader("Authorization");
if(!StringUtils.hasText(token) || !token.startsWith("Bearer")) {
filterChain.doFilter(request, response);
return;
}
token = token.substring(7);
String userid;
try{
Claims claims = JwtUtil.parseJWT(token);
userid = claims.getSubject();
} catch (Exception e) {
throw new RuntimeException(e);
}
UserBasicInfoVo user = new UserBasicInfoVo();
if (user == null) {
throw new RuntimeException("用户未登录");
}
JwtUser loginUser = new JwtUser(user);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, null);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request, response);
}
}

View File

@@ -0,0 +1,29 @@
package com.wuyiqi.netstateproc.model.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
public class UserInfoVo {
@Getter
@Setter
@ToString
public static class UserBasicInfoVo {
private Long id;
private String username;
private String email;
@JsonIgnore
private String password;
private Boolean isAdmin;
private Boolean isEnabled;
}
}

View File

@@ -0,0 +1,90 @@
package com.wuyiqi.netstateproc.security.config;
import com.wuyiqi.netstateproc.filter.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.WebSecurityCustomizer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
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;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Bean
public JwtAuthenticationTokenFilter authFilter() throws Exception {
return new JwtAuthenticationTokenFilter();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 前后端分离架构不需要csrf保护
.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
// 允许所有OPTIONS请求
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// 允许直接访问授权登录接口
//一般浏览器访问url为get请求而前端请求设置中为post增加安全
.requestMatchers(HttpMethod.POST, "/user/account/token/").permitAll()
// 允许 SpringMVC 的默认错误地址匿名访问
.requestMatchers("/error").permitAll()
// 其他所有接口必须有Authority信息Authority在登录成功后的UserDetailsImpl对象中默认设置“ROLE_USER”
//.requestMatchers("/**").hasAnyAuthority("ROLE_USER")
// 所有请求将会拦截,除了已经登录
.anyRequest().authenticated()
);
//这里默认为security登录页面在后续需要关闭时进行
// 添加 JWT 过滤器JWT 过滤器在用户名密码认证过滤器之前
http.addFilterBefore(authFilter(), UsernamePasswordAuthenticationFilter.class);
//使配置生效
return http.build();
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
// 将前端请求全双工放行
//使用WebSecurity.ignoring()忽略某些URL请求这些请求将被Spring Security忽略这意味着这些URL将有受到 CSRF、XSS、Clickjacking 等攻击的可能。
return (web) -> web.ignoring().requestMatchers("/websocket/**");
}
/**
* 配置跨源访问(CORS)
*
* @return
*/
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOriginPattern("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setAllowCredentials(true);
source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(source);
}
}

View File

@@ -0,0 +1,20 @@
package com.wuyiqi.netstateproc.security.service;
import com.wuyiqi.netstateproc.security.token.JwtUser;
import com.wuyiqi.netstateproc.service.UserInfoService;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@RequiredArgsConstructor
@Service("userDetailService")
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserInfoService userInfoService;
@Override
public JwtUser loadUserByUsername(String username) throws UsernameNotFoundException {
return null;
}
}

View File

@@ -0,0 +1,53 @@
package com.wuyiqi.netstateproc.security.token;
import com.wuyiqi.netstateproc.model.vo.UserInfoVo.UserBasicInfoVo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
@Data
@RequiredArgsConstructor
@AllArgsConstructor
public class JwtUser implements UserDetails {
private UserBasicInfoVo userBasicInfoVo;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return userBasicInfoVo.getPassword();
}
@Override
public String getUsername() {
return userBasicInfoVo.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return false;
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return userBasicInfoVo.getIsEnabled();
}
}

View File

@@ -0,0 +1,19 @@
package com.wuyiqi.netstateproc.service;
import org.springframework.stereotype.Service;
@Service
public interface UserInfoService {
void insertUser();
void deleteById(Long userId);
void restoreById(Long userId);
void updateEmail();
void updatePassword();
void updateForgetPassword();
}

View File

@@ -0,0 +1,63 @@
package com.wuyiqi.netstateproc.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;
@Component
public class JwtUtil {
public static final long JWT_TTL = 60 * 60 * 1000L * 24 * 2;
public static final String JWT_KEY = "ChengXuanWangZhaoLongWUYIQI666";
public static String getUUID() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
public static String createJWT(String subject) {
JwtBuilder builder = getJwtBuilder(subject, null, getUUID());
return builder.compact();
}
private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
SecretKey secretKey = generalKey();
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
if (ttlMillis == null) {
ttlMillis = JwtUtil.JWT_TTL;
}
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
return Jwts.builder()
.setId(uuid)
.setSubject(subject)
.setIssuer("sg")
.setIssuedAt(now)
.signWith(secretKey, signatureAlgorithm)
.setExpiration(expDate);
}
public static SecretKey generalKey() {
byte[] encodeKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
return new SecretKeySpec(encodeKey, 0, encodeKey.length, "HmacSHA256");
}
public static Claims parseJWT(String jwt) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJwt(jwt)
.getBody();
}
}