diff --git a/src/main/java/com/wuyiqi/netstateproc/filter/JwtAuthenticationTokenFilter.java b/src/main/java/com/wuyiqi/netstateproc/filter/JwtAuthenticationTokenFilter.java index 39a0ae9..aab574a 100644 --- a/src/main/java/com/wuyiqi/netstateproc/filter/JwtAuthenticationTokenFilter.java +++ b/src/main/java/com/wuyiqi/netstateproc/filter/JwtAuthenticationTokenFilter.java @@ -1,16 +1,22 @@ 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 { @@ -32,5 +38,19 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { } 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); + } } diff --git a/src/main/java/com/wuyiqi/netstateproc/model/vo/UserInfoVo.java b/src/main/java/com/wuyiqi/netstateproc/model/vo/UserInfoVo.java new file mode 100644 index 0000000..46faf98 --- /dev/null +++ b/src/main/java/com/wuyiqi/netstateproc/model/vo/UserInfoVo.java @@ -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; + } + +} diff --git a/src/main/java/com/wuyiqi/netstateproc/security/config/SecurityConfig.java b/src/main/java/com/wuyiqi/netstateproc/security/config/SecurityConfig.java index 08e8ce1..b5914af 100644 --- a/src/main/java/com/wuyiqi/netstateproc/security/config/SecurityConfig.java +++ b/src/main/java/com/wuyiqi/netstateproc/security/config/SecurityConfig.java @@ -1,12 +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; + + @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); + } } diff --git a/src/main/java/com/wuyiqi/netstateproc/security/service/UserDetailsServiceImpl.java b/src/main/java/com/wuyiqi/netstateproc/security/service/UserDetailsServiceImpl.java new file mode 100644 index 0000000..9e4c1d9 --- /dev/null +++ b/src/main/java/com/wuyiqi/netstateproc/security/service/UserDetailsServiceImpl.java @@ -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; + } +} diff --git a/src/main/java/com/wuyiqi/netstateproc/security/token/JwtUser.java b/src/main/java/com/wuyiqi/netstateproc/security/token/JwtUser.java new file mode 100644 index 0000000..c903f96 --- /dev/null +++ b/src/main/java/com/wuyiqi/netstateproc/security/token/JwtUser.java @@ -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 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(); + } +} diff --git a/src/main/java/com/wuyiqi/netstateproc/service/UserInfoService.java b/src/main/java/com/wuyiqi/netstateproc/service/UserInfoService.java new file mode 100644 index 0000000..b9ac9f0 --- /dev/null +++ b/src/main/java/com/wuyiqi/netstateproc/service/UserInfoService.java @@ -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(); +}