当前位置: 首页> 健康> 知识 > 电子商务网站规划、电子商务网站建设_长春seo公司哪家好_小辉seo_百度网盘客服在线咨询

电子商务网站规划、电子商务网站建设_长春seo公司哪家好_小辉seo_百度网盘客服在线咨询

时间:2025/7/9 0:15:11来源:https://blog.csdn.net/qq_45722630/article/details/144462312 浏览次数:0次
电子商务网站规划、电子商务网站建设_长春seo公司哪家好_小辉seo_百度网盘客服在线咨询

1. SpringSecurity介绍

Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。它是为Java应用程序设计的,特别是那些基于Spring的应用程序。Spring Security是一个社区驱动的开源项目,它提供了全面的安全性解决方案,包括防止常见的安全漏洞如CSRF、点击劫持、会话固定等。

以下是Spring Security的一些关键特性和概念:

  1. 认证(Authentication):Spring Security可以处理用户的身份验证过程,即确认用户是否是他们声称的人。它可以使用多种机制来进行身份验证,例如表单登录、HTTP基本认证、OAuth2、JWT等。

  2. 授权(Authorization):一旦用户通过了身份验证,Spring Security就会根据用户的权限来决定他们可以访问哪些资源。这可以通过定义角色、权限或更细粒度的访问规则来实现。

  3. 安全配置:Spring Security可以通过Java配置或XML配置来设置安全策略。通常推荐使用Java配置,因为它与现代Spring应用更为集成,并提供编译时检查。

  4. 拦截URL模式:可以定义哪些URL需要特定的权限才能访问,以及如何处理未认证或未经授权的请求。

  5. 过滤器链:Spring Security利用了一组过滤器(Filter),这些过滤器在每次HTTP请求时被调用,以执行各种安全相关的任务。开发者可以根据需要添加自定义过滤器。

  6. 密码编码:为了安全存储用户密码,Spring Security支持多种加密方式,如BCrypt、PBKDF2等。

  7. 记住我(Remember-Me):允许系统在用户关闭浏览器后仍然保持登录状态,直到明确登出或cookie过期。

  8. 注销(Logout):提供了安全的退出机制,确保用户的会话被正确地销毁。

  9. CSRF保护:默认启用跨站请求伪造攻击防护,确保只有来自合法来源的请求才能修改服务器端的状态。

  10. Session管理:可以配置会话创建策略,例如只在需要时创建会话,或者限制同一时间内的并发会话数量。

  11. OAuth2和OpenID Connect支持:内置对OAuth2客户端和资源服务器的支持,方便集成第三方认证服务。

使用Spring Security,开发者可以专注于业务逻辑的开发,而将安全问题交给这个成熟可靠的框架来处理。同时,由于其高度可扩展性和灵活性,Spring Security也适合用于构建复杂的安全需求。

2. 登录流程

登录API无需拦截,SpringSecurity直接放行。

/*** @description 认证授权**/
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Api(tags = "认证")
public class AuthController {private final AuthService authService;@PostMapping("/login")@ApiOperation("登录")public ResponseEntity<Void> login(@RequestBody LoginRequest loginRequest) {String token = authService.createToken(loginRequest);HttpHeaders httpHeaders = new HttpHeaders();httpHeaders.set(SecurityConstants.TOKEN_HEADER, token);return new ResponseEntity<>(httpHeaders, HttpStatus.OK);}
}

AuthService首先会校验用户名与密码,和用户的角色,然后调用JwtTokenUtils创建token,然后以userId为key,token作为value存在Redis中。

@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class AuthService {private final UserService userService;private final StringRedisTemplate stringRedisTemplate;private final CurrentUserUtils currentUserUtils;public String createToken(LoginRequest loginRequest) {User user = userService.find(loginRequest.getUsername());if (!userService.check(loginRequest.getPassword(), user.getPassword())) {throw new BadCredentialsException("The user name or password is not correct.");}JwtUser jwtUser = new JwtUser(user);if (!jwtUser.isEnabled()) {throw new BadCredentialsException("User is forbidden to login");}List<String> authorities = jwtUser.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());String token = JwtTokenUtils.createToken(user.getUserName(), user.getId().toString(), authorities, loginRequest.getRememberMe());stringRedisTemplate.opsForValue().set(user.getId().toString(), token);return token;}public void removeToken() {stringRedisTemplate.delete(currentUserUtils.getCurrentUser().getId().toString());}
}

JwtTokenUtils负责创建token、解析token与获取userId。

public class JwtTokenUtils {/*** 生成足够的安全随机密钥,以适合符合规范的签名*/private static final byte[] API_KEY_SECRET_BYTES = DatatypeConverter.parseBase64Binary(SecurityConstants.JWT_SECRET_KEY);private static final SecretKey SECRET_KEY = Keys.hmacShaKeyFor(API_KEY_SECRET_BYTES);public static String createToken(String username, String id, List<String> roles, boolean isRememberMe) {long expiration = isRememberMe ? SecurityConstants.EXPIRATION_REMEMBER : SecurityConstants.EXPIRATION;final Date createdDate = new Date();final Date expirationDate = new Date(createdDate.getTime() + expiration * 1000);String tokenPrefix = Jwts.builder().setHeaderParam("type", SecurityConstants.TOKEN_TYPE).signWith(SECRET_KEY, SignatureAlgorithm.HS256).claim(SecurityConstants.ROLE_CLAIMS, String.join(",", roles)).setId(id).setIssuer("SnailClimb").setIssuedAt(createdDate).setSubject(username).setExpiration(expirationDate).compact();return SecurityConstants.TOKEN_PREFIX + tokenPrefix; // 添加 token 前缀 "Bearer ";}// userIdpublic static String getId(String token) {Claims claims = getClaims(token);return claims.getId();}// 得到 userName、token与 authoritiespublic static UsernamePasswordAuthenticationToken getAuthentication(String token) {Claims claims = getClaims(token);List<SimpleGrantedAuthority> authorities = getAuthorities(claims);String userName = claims.getSubject();return new UsernamePasswordAuthenticationToken(userName, token, authorities);}private static List<SimpleGrantedAuthority> getAuthorities(Claims claims) {String role = (String) claims.get(SecurityConstants.ROLE_CLAIMS);return Arrays.stream(role.split(",")).map(SimpleGrantedAuthority::new).collect(Collectors.toList());}private static Claims getClaims(String token) {return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();}
}

3. JWT认证流程

// 启用 SpringSecurity
@EnableWebSecurity
// 启用 SpringSecurity 注解开发
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {private final StringRedisTemplate stringRedisTemplate;public SecurityConfiguration(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}/*** 密码编码器*/@Beanpublic BCryptPasswordEncoder bCryptPasswordEncoder() {return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.cors(withDefaults())// 禁用 CSRF.csrf().disable().authorizeRequests()// 指定的接口直接放行// swagger.antMatchers(SecurityConstants.SWAGGER_WHITELIST).permitAll().antMatchers(SecurityConstants.H2_CONSOLE).permitAll().antMatchers(HttpMethod.POST, SecurityConstants.SYSTEM_WHITELIST).permitAll()// 其他的接口都需要认证后才能请求.anyRequest().authenticated().and()//添加自定义Filter.addFilter(new JwtAuthorizationFilter(authenticationManager(), stringRedisTemplate))// 不需要session(不创建会话).sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()// 授权异常处理.exceptionHandling().authenticationEntryPoint(new JwtAuthenticationEntryPoint()).accessDeniedHandler(new JwtAccessDeniedHandler());// 防止H2 web 页面的Frame 被拦截http.headers().frameOptions().disable();}/*** Cors配置优化**/@BeanCorsConfigurationSource corsConfigurationSource() {org.springframework.web.cors.CorsConfiguration configuration = new CorsConfiguration();configuration.setAllowedOrigins(singletonList("*"));// configuration.setAllowedOriginPatterns(singletonList("*"));configuration.setAllowedHeaders(singletonList("*"));configuration.setAllowedMethods(Arrays.asList("GET", "POST", "DELETE", "PUT", "OPTIONS"));configuration.setExposedHeaders(singletonList(SecurityConstants.TOKEN_HEADER));configuration.setAllowCredentials(false);configuration.setMaxAge(3600L);UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", configuration);return source;}
}

自定义Filter

@Slf4j
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {private final StringRedisTemplate stringRedisTemplate;// 不是 Bean, 需要手动注入public JwtAuthorizationFilter(AuthenticationManager authenticationManager, StringRedisTemplate stringRedisTemplate) {super(authenticationManager);this.stringRedisTemplate = stringRedisTemplate;}@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain chain) throws IOException, ServletException {String token = request.getHeader(SecurityConstants.TOKEN_HEADER);if (token == null || !token.startsWith(SecurityConstants.TOKEN_PREFIX)) {SecurityContextHolder.clearContext();chain.doFilter(request, response);return;}String tokenValue = token.replace(SecurityConstants.TOKEN_PREFIX, "");UsernamePasswordAuthenticationToken authentication = null;try {// token是否有效String previousToken = stringRedisTemplate.opsForValue().get(JwtTokenUtils.getId(tokenValue));if (!token.equals(previousToken)) {SecurityContextHolder.clearContext();chain.doFilter(request, response);return;}authentication = JwtTokenUtils.getAuthentication(tokenValue);} catch (JwtException e) {logger.error("Invalid jwt : " + e.getMessage());}// 将userName, token, authorities保存在Context中SecurityContextHolder.getContext().setAuthentication(authentication);chain.doFilter(request, response);}
}

SecurityContextHolder是基于ThreadLocal实现的,可以实现不同线程之间的隔离。

public class SecurityContextHolder {public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";public static final String MODE_GLOBAL = "MODE_GLOBAL";public static final String SYSTEM_PROPERTY = "spring.security.strategy";private static String strategyName = System.getProperty("spring.security.strategy");private static SecurityContextHolderStrategy strategy;private static int initializeCount = 0;public SecurityContextHolder() {}private static void initialize() {if (!StringUtils.hasText(strategyName)) {strategyName = "MODE_THREADLOCAL";}if (strategyName.equals("MODE_THREADLOCAL")) {strategy = new ThreadLocalSecurityContextHolderStrategy();} else if (strategyName.equals("MODE_INHERITABLETHREADLOCAL")) {strategy = new InheritableThreadLocalSecurityContextHolderStrategy();} else if (strategyName.equals("MODE_GLOBAL")) {strategy = new GlobalSecurityContextHolderStrategy();} else {try {Class<?> clazz = Class.forName(strategyName);Constructor<?> customStrategy = clazz.getConstructor();strategy = (SecurityContextHolderStrategy)customStrategy.newInstance();} catch (Exception var2) {Exception ex = var2;ReflectionUtils.handleReflectionException(ex);}}++initializeCount;}
}

4. 全局异常处理器

public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {/*** 当用户尝试访问需要权限才能的REST资源而不提供Token或者Token错误或者过期时,* 将调用此方法发送401响应以及错误信息*/@Overridepublic void commence(HttpServletRequest request,HttpServletResponse response,AuthenticationException authException) throws IOException {response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());}
}
public class JwtAccessDeniedHandler implements AccessDeniedHandler {/*** 当用户尝试访问需要权限才能的REST资源而权限不足的时候,* 将调用此方法发送403响应以及错误信息*/@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {accessDeniedException = new AccessDeniedException("Sorry you don not enough permissions to access it!");response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage());}
}

5. 注销流程

删除Redis中保存的token。

@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class AuthService {private final UserService userService;private final StringRedisTemplate stringRedisTemplate;private final CurrentUserUtils currentUserUtils;public String createToken(LoginRequest loginRequest) {User user = userService.find(loginRequest.getUsername());if (!userService.check(loginRequest.getPassword(), user.getPassword())) {throw new BadCredentialsException("The user name or password is not correct.");}JwtUser jwtUser = new JwtUser(user);if (!jwtUser.isEnabled()) {throw new BadCredentialsException("User is forbidden to login");}List<String> authorities = jwtUser.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());String token = JwtTokenUtils.createToken(user.getUserName(), user.getId().toString(), authorities, loginRequest.getRememberMe());stringRedisTemplate.opsForValue().set(user.getId().toString(), token);return token;}public void removeToken() {stringRedisTemplate.delete(currentUserUtils.getCurrentUser().getId().toString());}
}
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CurrentUserUtils {private final UserService userService;public User getCurrentUser() {return userService.find(getCurrentUserName());}private  String getCurrentUserName() {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication != null && authentication.getPrincipal() != null) {return (String) authentication.getPrincipal();}return null;}
}

6. 权限管理

基于@PreAuthorize实现权限管理

@RestController
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@RequestMapping("/users")
@Api(tags = "用户")
public class UserController {private final UserService userService;@GetMapping// 有任意角色的权限都可以访问@PreAuthorize("hasAnyRole('ROLE_USER','ROLE_MANAGER','ROLE_ADMIN')")@ApiOperation("获取所有用户的信息(分页)")public ResponseEntity<Page<UserRepresentation>> getAllUser(@RequestParam(value = "pageNum", defaultValue = "0") int pageNum, @RequestParam(value = "pageSize", defaultValue = "10") int pageSize) {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();System.out.println("auth信息: " + authentication.getPrincipal().toString() + " 鉴权" + authentication.getAuthorities().toString());System.out.println("***********");Page<UserRepresentation> allUser = userService.getAll(pageNum, pageSize);return ResponseEntity.ok().body(allUser);}@PutMapping@PreAuthorize("hasAnyRole('ROLE_ADMIN')")@ApiOperation("更新用户")public ResponseEntity<Void> update(@RequestBody @Valid UserUpdateRequest userUpdateRequest) {userService.update(userUpdateRequest);return ResponseEntity.ok().build();}@DeleteMapping@PreAuthorize("hasAnyRole('ROLE_ADMIN')")@ApiOperation("根据用户名删除用户")public ResponseEntity<Void> deleteUserByUserName(@RequestParam("username") String username) {userService.delete(username);return ResponseEntity.ok().build();}
}
关键字:电子商务网站规划、电子商务网站建设_长春seo公司哪家好_小辉seo_百度网盘客服在线咨询

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: