diff --git a/pom.xml b/pom.xml
index f0630e5..b16971f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,18 +9,54 @@
cn.teammodel
- deployment-test
- 0.0.1-SNAPSHOT
- deployment-test
- deployment-test
+ Teammodel-extension
+ 1.0.1
+ Teammodel-extension
+ Teammodel-extension
1.8
+ 4.11.0
+
org.springframework.boot
spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-resource-server
+
+
+
+
+ com.azure.spring
+ spring-cloud-azure-starter
+
+
+
+ com.azure.spring
+ spring-cloud-azure-starter-data-cosmos
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ io.jsonwebtoken
+ jjwt
+ 0.9.1
+
org.springframework.boot
@@ -29,11 +65,31 @@
+
+
+
+ com.azure.spring
+ spring-cloud-azure-dependencies
+ ${spring-cloud-azure.version}
+ pom
+ import
+
+
+
+
org.springframework.boot
spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
diff --git a/src/main/java/cn/teammodel/controller/HelloController.java b/src/main/java/cn/teammodel/controller/HelloController.java
index 40c0dfe..5740065 100644
--- a/src/main/java/cn/teammodel/controller/HelloController.java
+++ b/src/main/java/cn/teammodel/controller/HelloController.java
@@ -1,16 +1,20 @@
package cn.teammodel.controller;
import cn.teammodel.common.R;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/")
public class HelloController {
@GetMapping("hello")
- public R helo() {
- return new R(200, "sucess","hello world");
+ @PreAuthorize("hasAuthority('IES')")
+ public R hello() {
+ System.out.println(SecurityContextHolder.getContext().getAuthentication());
+ return new R(200, "success","hello world");
}
}
\ No newline at end of file
diff --git a/src/main/java/cn/teammodel/security/SecurityConfiguration.java b/src/main/java/cn/teammodel/security/SecurityConfiguration.java
new file mode 100644
index 0000000..32fb123
--- /dev/null
+++ b/src/main/java/cn/teammodel/security/SecurityConfiguration.java
@@ -0,0 +1,65 @@
+package cn.teammodel.security;
+
+import cn.teammodel.security.filter.AuthInnerTokenFilter;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+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.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
+import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
+import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfiguration {
+ @Resource
+ private AuthInnerTokenFilter authInnerTokenFilter;
+
+ @Bean
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+ http
+ // CSRF禁用,因为不使用session
+ .csrf().disable()
+ // 禁用HTTP响应标头
+ .headers().cacheControl().disable()
+ .and()
+ .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+ .and()
+ .authorizeRequests(authorizeRequests ->
+ authorizeRequests
+ .antMatchers("/public/**").permitAll()
+ .anyRequest().authenticated()
+ )
+ .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt) // 启用 OIDC jwt filter
+ .addFilterAfter(authInnerTokenFilter, BearerTokenAuthenticationFilter.class); // 添加 x-auth-authToken filter
+ // todo: 失败处理器
+
+ return http.build();
+ }
+
+ /**
+ * OIDC: 将 jwt 中的 claim 中的 roles 放进 authorities 中
+ *
+ * @author: winter
+ * @date: 2023/11/8 17:13
+ */
+ @Bean
+ public JwtAuthenticationConverter jwtAuthenticationConverter() {
+ JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
+ grantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
+ grantedAuthoritiesConverter.setAuthorityPrefix("");
+
+ JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
+ jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
+ return jwtAuthenticationConverter;
+ }
+}
diff --git a/src/main/java/cn/teammodel/security/filter/AuthInnerTokenFilter.java b/src/main/java/cn/teammodel/security/filter/AuthInnerTokenFilter.java
new file mode 100644
index 0000000..b1f6eb4
--- /dev/null
+++ b/src/main/java/cn/teammodel/security/filter/AuthInnerTokenFilter.java
@@ -0,0 +1,23 @@
+package cn.teammodel.security.filter;
+
+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;
+
+/**
+ * x-auth-authToken filter
+ * @author winter
+ * @create 2023-11-09 10:43
+ */
+@Component
+public class AuthInnerTokenFilter extends OncePerRequestFilter {
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+ filterChain.doFilter(request, response);
+ }
+}
diff --git a/src/main/java/cn/teammodel/security/utils/JwtTokenUtil.java b/src/main/java/cn/teammodel/security/utils/JwtTokenUtil.java
new file mode 100644
index 0000000..16bb8c1
--- /dev/null
+++ b/src/main/java/cn/teammodel/security/utils/JwtTokenUtil.java
@@ -0,0 +1,115 @@
+package cn.teammodel.security.utils;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author winter
+ * @date 2022年11月24日 下午10:50
+ * @description 生成jwt令牌的工具类
+ */
+@Component
+@Slf4j
+public class JwtTokenUtil {
+ private static final String CLAIM_KEY_USERNAME = "sub";
+ private static final String CLAIM_KEY_CREATED = "created";
+
+ @Value("${jwt.secret}")
+ private String secret;
+ @Value("${jwt.expiration}")
+ private Long expiration;
+ @Value("${jwt.tokenHead}")
+ private String tokenHead;
+
+ /**
+ * 生成token
+ * @param userDetails 传入userDetails
+ * @return token
+ */
+ public String generateToken(UserDetails userDetails){
+ Map claims = new HashMap<>();
+ claims.put(CLAIM_KEY_USERNAME,userDetails.getUsername());
+ claims.put(CLAIM_KEY_CREATED,new Date());
+ return generateToken(claims);
+ }
+
+ private String generateToken(Map claims){
+ return Jwts.builder()
+ .setClaims(claims)
+ .setExpiration(getExpirationDate())
+ .signWith(SignatureAlgorithm.HS512,secret)
+ .compact();
+ }
+
+ /**
+ * 生成token过期时间
+ * @return 过期时间
+ */
+ private Date getExpirationDate() {
+ return new Date(System.currentTimeMillis() + expiration * 1000);
+ }
+
+ /**
+ * 从token中获取用户名
+ * @param token 前端传入的token
+ * @return 负载中的用户名,获取失败返回null
+ */
+ public String getUsernameFromToken(String token) {
+ String username = null;
+ try {
+ Claims claims = getClaimsFromToken(token);
+ username = claims.getSubject();
+ }catch (Exception e){
+ log.info("jwt验证出错:{}",token);
+ }
+ return username;
+ }
+
+ /**
+ * 从token中获取Claims,异常返回null
+ * @param token token
+ * @return claims或者null
+ */
+ private Claims getClaimsFromToken(String token){
+ Claims claims = null;
+ try {
+ claims = Jwts.parser()
+ .setSigningKey(secret)
+ .parseClaimsJws(token)
+ .getBody();
+ } catch (Exception e){
+ e.printStackTrace();
+ log.info("jwt解析出错:{}",token);
+ }
+ return claims;
+ }
+
+ /**
+ * 检验token是否还有效
+ * @param token 前端传入的token
+ * @param userDetails 登录用户细节
+ * @return true表示有效,false表示无效
+ */
+ public boolean validateToken(String token) {
+ return !isTokenExpired(token);
+ }
+
+ private boolean isTokenExpired(String token) {
+ Date expiredDate = getExpiredDateFromToken(token);
+ return expiredDate.before(new Date());
+ }
+
+ private Date getExpiredDateFromToken(String token) {
+ Claims claims = getClaimsFromToken(token);
+ return claims.getExpiration();
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
deleted file mode 100644
index 8b13789..0000000
--- a/src/main/resources/application.properties
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
new file mode 100644
index 0000000..93c87b7
--- /dev/null
+++ b/src/main/resources/application.yml
@@ -0,0 +1,14 @@
+spring:
+ cloud:
+ azure:
+ cosmos:
+ endpoint: https://cdhabookdep-free.documents.azure.cn:443
+ database: TEAMModelOS
+ key: v07dMthUjNk8AbwI8698AXHPbXBaaADgtgdYf4sFGXohei6aD2doq6XV45sLMGpDtDDpENAnlGkEUBu9RaAhpg==
+
+ security:
+ oauth2:
+ resourceserver:
+ jwt:
+ issuer-uri: https://login.partner.microsoftonline.cn/4807e9cf-87b8-4174-aa5b-e76497d7392b/v2.0
+ audiences: 72643704-b2e7-4b26-b881-bd5865e7a7a5
\ No newline at end of file