Просмотр исходного кода

feat: application security framework done

刘一也 4 лет назад
Родитель
Сommit
b87e9e28d7

+ 2 - 1
src/main/java/cn/seecoder/devmanage/DevmanageApplication.java

@@ -2,8 +2,9 @@ package cn.seecoder.devmanage;
 
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
 
-@SpringBootApplication
+@SpringBootApplication(exclude= {UserDetailsServiceAutoConfiguration.class})
 public class DevmanageApplication {
 
 	public static void main(String[] args) {

+ 14 - 0
src/main/java/cn/seecoder/devmanage/controller/HelloController.java

@@ -0,0 +1,14 @@
+package cn.seecoder.devmanage.controller;
+
+import cn.seecoder.devmanage.model.vo.Response;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class HelloController {
+    @GetMapping("/hello")
+    public Response<String> hello() {
+        return Response.buildSuccess("Hello Seecoder");
+    }
+}

+ 19 - 0
src/main/java/cn/seecoder/devmanage/model/po/Test.java

@@ -0,0 +1,19 @@
+package cn.seecoder.devmanage.model.po;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+import static javax.persistence.GenerationType.AUTO;
+
+@Entity
+@Getter
+@Setter
+public class Test {
+    @Id
+    @GeneratedValue(strategy = AUTO)
+    private Long id;
+}

+ 26 - 0
src/main/java/cn/seecoder/devmanage/model/vo/Response.java

@@ -0,0 +1,26 @@
+package cn.seecoder.devmanage.model.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@AllArgsConstructor
+public class Response<T> implements Serializable {
+    private Integer code;
+    private String msg;
+    private T data;
+
+    public static Response<Object> buildSuccess() {
+        return new Response<>(200, "", null);
+    }
+
+    public static <T> Response<T> buildSuccess(T result) {
+        return new Response<>(200, "", result);
+    }
+
+    public static  Response<Object> buildFailure(Integer code, String message) {
+        return new Response<>(code, message, null);
+    }
+}

+ 7 - 0
src/main/java/cn/seecoder/devmanage/repository/TestRepo.java

@@ -0,0 +1,7 @@
+package cn.seecoder.devmanage.repository;
+
+import cn.seecoder.devmanage.model.po.Test;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface TestRepo extends JpaRepository<Test, Long> {
+}

+ 69 - 0
src/main/java/cn/seecoder/devmanage/security/JwtAuthenticationFilter.java

@@ -0,0 +1,69 @@
+package cn.seecoder.devmanage.security;
+
+import cn.seecoder.devmanage.model.vo.Response;
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.springframework.http.HttpHeaders.AUTHORIZATION;
+import static org.springframework.http.HttpStatus.FORBIDDEN;
+import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
+
+@Component
+@RequiredArgsConstructor
+@Slf4j
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+    private final JwtHelper jwtHelper;
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+        String authorizationHeader = request.getHeader(AUTHORIZATION);
+        if (authorizationHeader != null) {
+            try {
+                Algorithm algorithm = jwtHelper.getAlgorithm();
+                JWTVerifier verifier = JWT.require(algorithm).build();
+                DecodedJWT decodedJWT = verifier.verify(authorizationHeader);
+                String username = decodedJWT.getSubject();  // also phone number
+                String authority = decodedJWT.getClaim("auth").asString();
+                Collection<GrantedAuthority> authorities = new ArrayList<>();
+                authorities.add(new SimpleGrantedAuthority(authority));
+                UsernamePasswordAuthenticationToken authenticationToken =
+                        new UsernamePasswordAuthenticationToken(username, null, authorities);
+                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
+                filterChain.doFilter(request, response);
+            } catch (Exception e) {
+                log.error("Error JWT parsing or verifying: {}", e.getMessage());
+                response.setStatus(FORBIDDEN.value());
+                response.setContentType(APPLICATION_JSON_VALUE);
+                new ObjectMapper().writeValue(response.getOutputStream(),
+                        Response.buildFailure(FORBIDDEN.value(), e.getMessage()));
+            }
+        } else {
+            log.error("Request lacks Authorization header.");
+            response.setStatus(FORBIDDEN.value());
+            response.setContentType(APPLICATION_JSON_VALUE);
+            new ObjectMapper().writeValue(response.getOutputStream(),
+                    Response.buildFailure(FORBIDDEN.value(), "Lack of Authorization header."));
+        }
+    }
+}

+ 21 - 0
src/main/java/cn/seecoder/devmanage/security/JwtHelper.java

@@ -0,0 +1,21 @@
+package cn.seecoder.devmanage.security;
+
+import com.auth0.jwt.algorithms.Algorithm;
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+
+@Component
+@Getter
+public class JwtHelper {
+    @Value("${seecoder.jwt.secret}")
+    private String secret;
+    private Algorithm algorithm;
+
+    @PostConstruct
+    public void init() {
+        algorithm = Algorithm.HMAC256(secret);
+    }
+}

+ 32 - 0
src/main/java/cn/seecoder/devmanage/security/SecurityConfig.java

@@ -0,0 +1,32 @@
+package cn.seecoder.devmanage.security;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+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.WebSecurityConfigurerAdapter;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
+
+@Configuration
+@EnableWebSecurity
+@RequiredArgsConstructor
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+    private final JwtAuthenticationFilter jwtAuthenticationFilter;
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http
+                .csrf().disable()
+                .sessionManagement().sessionCreationPolicy(STATELESS)
+                .and()
+                .authorizeRequests()
+                .anyRequest().hasAnyAuthority("SEEC", "ADMIN")
+                .and()
+                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
+    }
+}

+ 13 - 0
src/main/resources/application-lyy.yaml

@@ -0,0 +1,13 @@
+spring:
+  datasource:
+    url: jdbc:mysql://localhost:3306/devmanage?useUnicode=true&character_set_server=utf8mb4
+    username: root
+    password: 114514
+    driver-class-name: com.mysql.cj.jdbc.Driver
+  jpa:
+    hibernate:
+      ddl-auto: update
+    show-sql: true
+seecoder:
+  jwt:
+    secret: testsecret

+ 0 - 1
src/main/resources/application.properties

@@ -1 +0,0 @@
-

+ 6 - 0
src/main/resources/application.yaml

@@ -0,0 +1,6 @@
+spring:
+  profiles:
+    active: prod
+server:
+  servlet:
+    context-path: /api