赞
踩
访问1:localhost:8080/security,返回:需要先认证才能访问(说明没有权限)
访问2:localhost:8080/anonymous,返回:anonymous(说明正常访问)
相关文件如下:
pom.xml:
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>3.2.4</version>
- <relativePath/> <!-- lookup parent from repository -->
- </parent>
-
-
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-security</artifactId>
- </dependency>
-
- <dependency>
- <groupId>cn.hutool</groupId>
- <artifactId>hutool-all</artifactId>
- <version>5.8.18</version>
- </dependency>

WebSecurityConfiguration:
-
-
- /**
- * Spring Security 配置项
- */
- @Configuration
- @EnableWebSecurity
- @EnableMethodSecurity
- @EnableGlobalAuthentication
- public class WebSecurityConfiguration {
-
- @Autowired
- private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
-
- @Autowired
- private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
-
- @Autowired
- private RestAccessDeniedHandler restAccessDeniedHandler;
-
- private UserDetailsService userDetailsService;
-
- @Autowired
- private ApplicationContext applicationContext;
-
-
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
-
- // 搜寻 匿名标记 url: PreAuthorize("hasAnyRole('anonymous')") 和 PreAuthorize("@tsp.check('anonymous')") 和 AnonymousAccess
- Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
- Set<String> anonymousUrls = new HashSet<>();
- for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) {
- HandlerMethod handlerMethod = infoEntry.getValue();
- AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
- PreAuthorize preAuthorize = handlerMethod.getMethodAnnotation(PreAuthorize.class);
- PathPatternsRequestCondition pathPatternsCondition = infoEntry.getKey().getPathPatternsCondition();
- Set<String> patternList = new HashSet<>();
- if (null != pathPatternsCondition){
- Set<PathPattern> patterns = pathPatternsCondition.getPatterns();
- for (PathPattern pattern : patterns) {
- patternList.add(pattern.getPatternString());
- }
- }
- if (null != preAuthorize && preAuthorize.value().toLowerCase().contains("anonymous")) {
- anonymousUrls.addAll(patternList);
- } else if (null != anonymousAccess && null == preAuthorize) {
- anonymousUrls.addAll(patternList);
- }
- }
-
- httpSecurity
- // 禁用basic明文验证
- .httpBasic(it -> it.disable())
- // 前后端分离架构不需要csrf保护
- .csrf(it -> it.disable())
- // 禁用默认登录页
- .formLogin(it -> it.disable())
- // 禁用默认登出页
- .logout(it -> it.disable())
- // 设置异常的EntryPoint,如果不设置,默认使用Http403ForbiddenEntryPoint
- .exceptionHandling(exceptions -> {
- // 401
- exceptions.authenticationEntryPoint(restAuthenticationEntryPoint);
- // 403
- exceptions.accessDeniedHandler(restAccessDeniedHandler);
- })
- // 前后端分离是无状态的,不需要session了,直接禁用。
- .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
- .authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
- // 允许匿名访问
- .requestMatchers(anonymousUrls.toArray(new String[0])).permitAll()
- // 允许所有OPTIONS请求
- .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
- // 允许 SpringMVC 的默认错误地址匿名访问
- .requestMatchers("/error").permitAll()
- // 允许任意请求被已登录用户访问,不检查Authority
- .anyRequest().authenticated())
- .authenticationProvider(authenticationProvider())
- // 加我们自定义的过滤器,替代UsernamePasswordAuthenticationFilter
- .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
-
- return httpSecurity.build();
- }
-
-
-
- @Bean
- public CorsFilter corsFilter() {
- UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
- CorsConfiguration config = new CorsConfiguration();
- // 允许所有域名进行跨域调用
- config.addAllowedOrigin("*");
- // 放行全部原始头信息
- config.addAllowedHeader("*");
- // 允许所有请求方法跨域调用
- config.addAllowedMethod("OPTIONS");
- config.addAllowedMethod("GET");
- config.addAllowedMethod("POST");
- config.addAllowedMethod("PUT");
- config.addAllowedMethod("DELETE");
- source.registerCorsConfiguration("/**", config);
- return new CorsFilter(source);
-
- }
- @Bean
- public UserDetailsService userDetailsService() {
- return username -> userDetailsService.loadUserByUsername(username);
- }
-
- @Bean
- public PasswordEncoder passwordEncoder() {
- return new BCryptPasswordEncoder();
- }
-
- @Bean
- public AuthenticationProvider authenticationProvider() {
- DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
- authProvider.setUserDetailsService(userDetailsService());
- // 设置密码编辑器
- authProvider.setPasswordEncoder(passwordEncoder());
- return authProvider;
- }
-
- }

JwtAuthenticationTokenFilter
-
-
- /**
- * JWT登录授权过滤器
- */
-
- @Component
- public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
-
- @Override
- protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
- @NonNull FilterChain chain) throws ServletException, IOException {
- String authorization = request.getHeader("Authorization");
- response.setCharacterEncoding("utf-8");
- if (null == authorization){
- // 没有token
- chain.doFilter(request, response);
- return;
- }
- try{
- if (!authorization.startsWith("Bearer ")){
- // token格式不正确
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "token格式不正确");
- return;
- }
- boolean verify = MyJWTUtil.verify(authorization);
- if(!verify){
- // token格式不正确
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "token验证失败");
- return;
- }
- }catch (Exception e){
- // token格式不正确
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "token验证失败");
- return;
- }
- JWT jwt = MyJWTUtil.parseToken(authorization);
- Object uid = jwt.getPayload("uid");
-
- // todo 解析JWT获取用户信息
- LoginUser loginUser = new LoginUser();
-
- UsernamePasswordAuthenticationToken authenticationToken =
- new UsernamePasswordAuthenticationToken(loginUser,null,null);
- SecurityContextHolder.getContext().setAuthentication(authenticationToken);
-
- chain.doFilter(request, response);
- }
-
- }

RestAuthenticationEntryPoint:
-
-
-
- /**
- * 认证失败处理类
- */
-
- @Component
- public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
-
- @Override
- public void commence(HttpServletRequest request, HttpServletResponse response,
- AuthenticationException authException) throws IOException {
- response.setHeader("Access-Control-Allow-Origin", "*");
- response.setHeader("Cache-Control", "no-cache");
- response.setContentType("application/json");
- response.setCharacterEncoding("UTF-8");
- response.setStatus(HttpStatus.UNAUTHORIZED.value());
- response.getWriter().println(authException == null? "Unauthorized" : "需要先认证才能访问");
- response.getWriter().flush();
-
- }
-
- }

RestAccessDeniedHandler:
-
-
- /**
- * 自定义无权访问处理类
- */
- @Component
- public class RestAccessDeniedHandler implements AccessDeniedHandler {
-
- @Override
- public void handle(HttpServletRequest request, HttpServletResponse response,
- AccessDeniedException accessDeniedException) throws IOException {
- response.setHeader("Access-Control-Allow-Origin", "*");
- response.setHeader("Cache-Control", "no-cache");
- response.setContentType("application/json");
- response.setCharacterEncoding("UTF-8");
- response.setStatus(HttpStatus.FORBIDDEN.value());
- // response.getWriter()
- // .println(accessDeniedException==null?"AccessDenied":accessDeniedException.getMessage());
- response.getWriter().println(accessDeniedException == null? "AccessDenied" : "没有访问权限");
- response.getWriter().flush();
- }
-
- }

AnonymousAccess:
-
-
- /**
- * 用于标记匿名访问方法
- */
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface AnonymousAccess {
-
- }
MyJWTUtil:
-
-
- /**
- * JWT工具类
- */
- public class MyJWTUtil extends JWTUtil {
-
-
- /**
- * 解析JWT Token
- *
- * @param token token
- * @return {@link JWT}
- */
- public static boolean verify(String token) {
- return verify(token, "LOGIN_TOKEN_KEY_20240410".getBytes());
- }
-
- /**
- * 解析JWT Token
- *
- * @param token token
- * @return {@link JWT}
- */
- public static boolean verify(String token, byte[] key) {
- if(StrUtil.isNotEmpty(token)){
- if(token.startsWith("Bearer ")){
- token = token.split("Bearer ")[1].trim();
- }
- }
- return JWT.of(token).setKey(key).verify();
- }
-
- /**
- * 解析JWT Token
- *
- * @param token token
- * @return {@link JWT}
- */
- public static JWT parseToken(String token) {
- if(StrUtil.isNotEmpty(token)){
- if(token.startsWith("Bearer ")){
- token = token.split("Bearer ")[1].trim();
- }
- }
- return JWT.of(token);
- }
-
- public static String getToken(HttpServletRequest request) {
- final String requestHeader = request.getHeader("Authorization");
- if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
- return requestHeader.substring(7);
- }
- return null;
- }
-
- public static String createToken(String userId) {
- Map<String, Object> payload = new HashMap<>(4);
- payload.put("uid", userId);
- payload.put("expire_time", System.currentTimeMillis() + 1000 * 60 * 60 * 8);
- return createToken(payload, "LOGIN_TOKEN_KEY_20240410".getBytes());
- }
- }

DemoController:
-
-
- @RestController
- public class DemoController {
-
-
- @GetMapping("anonymous")
- @AnonymousAccess
- public String loadCondition() {
- return "anonymous";
- }
-
- @GetMapping("security")
- public String security() {
- return "security";
- }
-
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。