diff --git a/src/main/java/com/example/config/SecurityConfiguration.java b/src/main/java/com/example/config/SecurityConfiguration.java index 484c581..c32e2c2 100644 --- a/src/main/java/com/example/config/SecurityConfiguration.java +++ b/src/main/java/com/example/config/SecurityConfiguration.java @@ -2,6 +2,7 @@ package com.example.config; import com.example.entity.RestBean; import com.example.entity.vo.response.AuthorizeV0; +import com.example.filter.JwtAuthorizeFilter; import com.example.utils.JwtUtils; import jakarta.annotation.Resource; import jakarta.servlet.ServletException; @@ -9,13 +10,17 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.access.AccessDeniedException; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.User; +import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import java.io.IOException; @@ -23,6 +28,9 @@ import java.io.IOException; public class SecurityConfiguration { @Resource JwtUtils utils; + + @Resource + JwtAuthorizeFilter jwtAuthorizeFilter; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http @@ -41,33 +49,57 @@ public class SecurityConfiguration { .logoutSuccessHandler(this::onLogoutSuccess) ) + .exceptionHandling(conf -> conf + .authenticationEntryPoint(this::onUnauthorized) + //用户登录了但没有权限访问一些资源 + .accessDeniedHandler(this::onAccessDeny) + ) + + + + .csrf(AbstractHttpConfigurer::disable) .sessionManagement(conf -> conf .sessionCreationPolicy(SessionCreationPolicy.STATELESS) ) + .addFilterBefore(jwtAuthorizeFilter , UsernamePasswordAuthenticationFilter.class) .build(); } + //403被禁止的处理 + public void onAccessDeny(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception) throws IOException, ServletException { + response.setContentType("application/jason;charset=utf-8"); + response.getWriter().write(RestBean.forbidden(exception.getMessage()).asJsonString()); + } + + + + private void onUnauthorized(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException { + response.setContentType("application/jason;charset=utf-8"); + response.getWriter().write(RestBean.unauthorized(e.getMessage()).asJsonString());} + + + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { response.setContentType("application/json;charset=utf-8"); User user = (User) authentication.getPrincipal(); - String token = utils.createJwt(user , 1 , "小明"); //随便起一个id和用户名 作为令牌的 + String token = utils.createJwt(user , 1 , "小明"); AuthorizeV0 v0 = new AuthorizeV0(); v0.setExpire(utils.expireTime()); v0.setRole(""); v0.setToken(token); v0.setUsername("小明"); - response.getWriter().write(RestBean.success(v0).asJsonString()); //将实体对象v0返回 + response.getWriter().write(RestBean.success(v0).asJsonString()); } public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { response.setContentType("application/json;charset=utf-8"); - response.getWriter().write(RestBean.failure(401 , exception.getMessage()).asJsonString()); + response.getWriter().write(RestBean.unauthorized( exception.getMessage()).asJsonString()); } diff --git a/src/main/java/com/example/controller/TestController.java b/src/main/java/com/example/controller/TestController.java new file mode 100644 index 0000000..6d9dbcd --- /dev/null +++ b/src/main/java/com/example/controller/TestController.java @@ -0,0 +1,15 @@ +package com.example.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/test") +public class TestController { + @GetMapping("/hello") + public String test(){ + return "Hello world"; + } + +} diff --git a/src/main/java/com/example/entity/RestBean.java b/src/main/java/com/example/entity/RestBean.java index d55b518..b9460d3 100644 --- a/src/main/java/com/example/entity/RestBean.java +++ b/src/main/java/com/example/entity/RestBean.java @@ -10,12 +10,21 @@ public record RestBean(int code , T data , String message) { } public static RestBean success(){ return success(null); + } + public static RestBean unauthorized(String message){ + return failure(401 , message); + } + + //403 + public static RestBean forbidden(String message){ + return failure(403 , message); } public static RestBean failure(int code , String message){ return new RestBean<>(code , null , message); } + public String asJsonString(){ return JSONObject.toJSONString(this, JSONWriter.Feature.WriteNulls); } diff --git a/src/main/java/com/example/filter/JwtAuthorizeFilter.java b/src/main/java/com/example/filter/JwtAuthorizeFilter.java new file mode 100644 index 0000000..a434d30 --- /dev/null +++ b/src/main/java/com/example/filter/JwtAuthorizeFilter.java @@ -0,0 +1,42 @@ +package com.example.filter; + +import com.auth0.jwt.interfaces.DecodedJWT; +import com.example.utils.JwtUtils; +import jakarta.annotation.Resource; +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.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +@Component +public class JwtAuthorizeFilter extends OncePerRequestFilter { + + @Resource + JwtUtils utils; + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + //从请求头中拿到token + String authorization = request.getHeader("Authorization"); + DecodedJWT jwt = utils.resolverJwt(authorization); + if(jwt != null){ //jwt不为空则进行解析 为空则进入下一个过滤器链 + UserDetails user = utils.toUser(jwt); + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken(user ,null,user.getAuthorities()); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authentication); + request.setAttribute("id",utils.toId(jwt)); + + } + filterChain.doFilter(request , response); + } +} diff --git a/src/main/java/com/example/utils/JwtUtils.java b/src/main/java/com/example/utils/JwtUtils.java index f8d0b88..2597b63 100644 --- a/src/main/java/com/example/utils/JwtUtils.java +++ b/src/main/java/com/example/utils/JwtUtils.java @@ -1,15 +1,21 @@ package com.example.utils; import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTVerificationException; +import com.auth0.jwt.interfaces.Claim; +import com.auth0.jwt.interfaces.DecodedJWT; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; import java.util.Calendar; import java.util.Date; +import java.util.Map; @Component public class JwtUtils { @@ -19,6 +25,47 @@ public class JwtUtils { @Value("${spring.security.jwt.expire}") int expire; + //解析jwt + public DecodedJWT resolverJwt(String headerToken){ + String token =this.convertToken(headerToken); + if (token == null ) return null; + Algorithm algorithm = Algorithm.HMAC256(key); + JWTVerifier jwtVerifier = JWT.require(algorithm).build(); + //验证jwt是否被用户篡改过,是的话抛出异常 + try { + DecodedJWT verify = jwtVerifier.verify(token); + Date expiresAt = verify.getExpiresAt(); //判断token是否过期 + return new Date().after(expiresAt) ? null : verify; + + } catch (JWTVerificationException e) { + return null; + } + } + + private String convertToken(String headerToken){ + if(headerToken == null || !headerToken.startsWith("Bearer")) //判断token是否合法 + return null; + return headerToken.substring(7); //返回切割掉Bearer之后的token + + + } + //解析Jwt中用户数据的方法 + public UserDetails toUser(DecodedJWT jwt){ + Map claims = jwt.getClaims(); + return User + .withUsername(claims.get("name").asString()) //存疑关于是name还是username + .password("******") + .authorities(claims.get("authorities").asArray(String.class)) + .build(); + } + //将jwt中的id转换为int数据 + public Integer toId(DecodedJWT jwt){ + Map claims = jwt.getClaims(); + return claims.get("id").asInt(); + } + + + public String createJwt(UserDetails details , int id , String username){ Algorithm algorithm = Algorithm.HMAC256(key); Date expire = this.expireTime();