Преглед изворни кода

feature: jwt and spring security implement permission isolation

ggbocoder пре 2 година
родитељ
комит
fa88b55c91

+ 22 - 1
pom.xml

@@ -56,6 +56,24 @@
             <artifactId>jakarta.mail-api</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>0.11.2</version> <!-- 使用最新版本 -->
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>0.11.2</version> <!-- 使用最新版本 -->
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-jackson</artifactId>
+            <version>0.11.2</version> <!-- 使用最新版本 -->
+            <scope>runtime</scope>
+        </dependency>
+
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-thymeleaf</artifactId>
@@ -65,7 +83,10 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-mail</artifactId>
         </dependency>
-
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
 
 
     </dependencies>

+ 2 - 1
src/main/java/cn/seecoder/fdroidrepository/Controller/CommentController.java

@@ -5,11 +5,12 @@ import cn.seecoder.fdroidrepository.Service.CommentService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
 
-// CommentController.java
+@PreAuthorize("hasRole('ROLE_ADMIN')")
 @RestController
 @RequestMapping("/api/comments")
 public class CommentController {

+ 2 - 0
src/main/java/cn/seecoder/fdroidrepository/Controller/ForumPostController.java

@@ -5,11 +5,13 @@ import cn.seecoder.fdroidrepository.Service.ForumPostService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
 
 @RestController
+@PreAuthorize("hasRole('ROLE_ADMIN')")
 @RequestMapping("/api/forum")
 public class ForumPostController {
     @Autowired

+ 36 - 8
src/main/java/cn/seecoder/fdroidrepository/Controller/UserController.java

@@ -5,12 +5,23 @@ package cn.seecoder.fdroidrepository.Controller;
 import cn.seecoder.fdroidrepository.DataObject.User;
 import cn.seecoder.fdroidrepository.Service.ServiceImpl.USER_STATUS;
 import cn.seecoder.fdroidrepository.Service.UserService;
-import cn.seecoder.fdroidrepository.utils.Result;
+import cn.seecoder.fdroidrepository.config.WebSecurityConfig;
+import cn.seecoder.fdroidrepository.result.Code;
+import cn.seecoder.fdroidrepository.result.SingleResult;
+import cn.seecoder.fdroidrepository.utils.JwtUtil;
+import cn.seecoder.fdroidrepository.result.Result;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
+
 
 @RestController
 @RequestMapping("/api/users")
@@ -18,12 +29,17 @@ public class UserController {
     @Autowired
     private UserService userService;
 
+    @Autowired
+    private JwtUtil jwtTokenUtils;
+
+    @Autowired
+    private AuthenticationManager authenticationManager;
+
     @PostMapping("/register")
     public ResponseEntity<String> register(@RequestBody User user) {
         if (!userService.isUsernameUnique(user.getUsername())) {
             return new ResponseEntity<>("Username is already taken", HttpStatus.BAD_REQUEST);
         }
-
         userService.save(user);
         return new ResponseEntity<>("User registered successfully", HttpStatus.OK);
     }
@@ -40,13 +56,25 @@ public class UserController {
     }
 
     @PostMapping("/login")
-    public ResponseEntity<String> login(@RequestBody User user) {
-        User existingUser = userService.findByUsername(user.getUsername());
+    public Result<String> login(HttpServletResponse response, @RequestBody User user) {
+        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
+            user.getUsername(), user.getPassword());
 
-        if (existingUser == null || !existingUser.getPassword().equals(user.getPassword())) {
-            return new ResponseEntity<>("Invalid username or password", HttpStatus.UNAUTHORIZED);
-        }
+        try {
+            //AuthenticationManager(default ProviderManager) #authenticate check Authentication
+            Authentication authentication = authenticationManager.authenticate(authenticationToken);
+            //bind authentication to securityContext
+            SecurityContextHolder.getContext().setAuthentication(authentication);
+            //create token
+            String token = jwtTokenUtils.createToken(authentication);
 
-        return new ResponseEntity<>("Login successful", HttpStatus.OK);
+            String authHeader = WebSecurityConfig.TOKEN_PREFIX + token;
+            //put token into http header
+            response.addHeader(WebSecurityConfig.AUTHORIZATION_HEADER, authHeader);
+
+            return SingleResult.success(authHeader);
+        } catch (BadCredentialsException authentication) {
+            return SingleResult.failure(Code.LOGIN_FAILED);
+        }
     }
 }

+ 1 - 0
src/main/java/cn/seecoder/fdroidrepository/DataObject/User.java

@@ -16,6 +16,7 @@ public class User {
     private String phoneNumber;
     private Date registrationDate;
     private boolean active;
+    // VISITOR, ADMIN
     private String role;
     private String activationCode;
     private Date createTime;

+ 55 - 4
src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/UserServiceImpl.java

@@ -4,20 +4,25 @@ package cn.seecoder.fdroidrepository.Service.ServiceImpl;
 import cn.seecoder.fdroidrepository.DataObject.User;
 import cn.seecoder.fdroidrepository.Mapper.UserMapper;
 import cn.seecoder.fdroidrepository.Service.UserService;
+import cn.seecoder.fdroidrepository.config.WebSecurityConfig;
 import cn.seecoder.fdroidrepository.utils.MailClient;
 import cn.seecoder.fdroidrepository.utils.Tools;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.stereotype.Service;
 
 import org.thymeleaf.TemplateEngine;
 import org.thymeleaf.context.Context;
 
-import java.util.Date;
-import java.util.UUID;
+import java.util.*;
 
 @Service
-public class UserServiceImpl implements UserService {
+public class UserServiceImpl implements UserService, UserDetailsService {
 
     @Value("${community.path.domain}")
     private String domain;
@@ -44,7 +49,7 @@ public class UserServiceImpl implements UserService {
 
         // register user
         user.setSalt(UUID.randomUUID().toString().substring(0,5));
-        user.setPassword(Tools.md5(user.getPassword()+user.getSalt()));
+        user.setPassword(WebSecurityConfig.passwordEncoder().encode(user.getPassword()));
         user.setActive(false);
         user.setCreateTime(new Date());
         user.setActivationCode(Tools.generateUUID());
@@ -79,4 +84,50 @@ public class UserServiceImpl implements UserService {
     }
 
 
+    @Override
+    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+        User user=findByUsername(username);
+        if(Objects.isNull(user)){
+            throw new UsernameNotFoundException("no user named "+username );
+        }
+        return new UserDetails() {
+            @Override
+            public Collection<? extends GrantedAuthority> getAuthorities() {
+                Set<GrantedAuthority> authorities = new HashSet<>();
+                // Assuming your User class has a getRoles() method that returns a collection of roles
+                authorities.add(new SimpleGrantedAuthority("ROLE_" + user.getRole()));
+                return authorities;
+            }
+
+            @Override
+            public String getPassword() {
+                return user.getPassword();
+            }
+
+            @Override
+            public String getUsername() {
+                return user.getUsername();
+            }
+
+            @Override
+            public boolean isAccountNonExpired() {
+                return true;
+            }
+
+            @Override
+            public boolean isAccountNonLocked() {
+                return true;
+            }
+
+            @Override
+            public boolean isCredentialsNonExpired() {
+                return true;
+            }
+
+            @Override
+            public boolean isEnabled() {
+                return true;
+            }
+        };
+    }
 }

+ 103 - 0
src/main/java/cn/seecoder/fdroidrepository/config/WebSecurityConfig.java

@@ -0,0 +1,103 @@
+package cn.seecoder.fdroidrepository.config;
+
+import cn.seecoder.fdroidrepository.Service.ServiceImpl.UserServiceImpl;
+import cn.seecoder.fdroidrepository.security.JwtAuthenticationEntryPoint;
+import cn.seecoder.fdroidrepository.security.JwtAuthenticationTokenFilter;
+import cn.seecoder.fdroidrepository.utils.JwtUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.BeanIds;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+@Configuration(proxyBeanMethods = false)
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+    /**
+     * The constant AUTHORIZATION_HEADER.
+     */
+    public static final String AUTHORIZATION_HEADER = "Authorization";
+
+    /**
+     * The constant AUTHORIZATION_TOKEN.
+     */
+    public static final String AUTHORIZATION_TOKEN = "access_token";
+
+    /**
+     * The constant SECURITY_IGNORE_URLS_SPILT_CHAR.
+     */
+    public static final String SECURITY_IGNORE_URLS_SPILT_CHAR = ",";
+
+    /**
+     * The constant TOKEN_PREFIX.
+     */
+    public static final String TOKEN_PREFIX = "Bearer ";
+
+    @Autowired
+    private UserServiceImpl userDetailsService;
+
+    @Autowired
+    private JwtAuthenticationEntryPoint unauthorizedHandler;
+
+    @Autowired
+    private JwtUtil tokenProvider;
+
+
+
+    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
+    @Override
+    public AuthenticationManager authenticationManagerBean() throws Exception {
+        return super.authenticationManagerBean();
+    }
+
+    @Override
+    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
+    }
+
+    @Override
+    public void configure(WebSecurity web) {
+        String ignoreURLs = "/public/**,/open-api/**,/static/**,/css/**,/js/**,/images/**,/api/users/**";
+        for (String ignoreURL : ignoreURLs.trim().split(SECURITY_IGNORE_URLS_SPILT_CHAR)) {
+            web.ignoring().antMatchers(ignoreURL.trim());
+        }
+    }
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http.authorizeRequests().anyRequest().authenticated().and()
+            // custom token authorize exception handler
+            .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
+            // since we use jwt, session is not necessary
+            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
+            // since we use jwt, csrf is not necessary
+            .csrf().disable();
+        http.addFilterBefore(new JwtAuthenticationTokenFilter(tokenProvider),
+            UsernamePasswordAuthenticationFilter.class);
+
+        // disable cache
+        http.headers().cacheControl();
+    }
+
+    /**
+     * Password encoder password encoder.
+     *
+     * @return the password encoder
+     */
+    @Bean
+    public static PasswordEncoder passwordEncoder() {
+        return new BCryptPasswordEncoder();
+    }
+
+}

+ 104 - 0
src/main/java/cn/seecoder/fdroidrepository/result/Code.java

@@ -0,0 +1,104 @@
+/*
+ *  Copyright 1999-2019 Seata.io Group.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package cn.seecoder.fdroidrepository.result;
+
+/**
+ * The Code for the response of message
+ *
+ * @author jameslcj
+ */
+public enum Code {
+    /**
+     * response success
+     */
+    SUCCESS("200", "ok"),
+    /**
+     * server error
+     */
+    ERROR("500", "Server error"),
+    /**
+     * the custom error
+     */
+    LOGIN_FAILED("401", "Login failed");
+
+    /**
+     * The Code.
+     */
+    public String code;
+
+    /**
+     * The Msg.
+     */
+    public String msg;
+
+    private Code(String code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    /**
+     * Gets code.
+     *
+     * @return the code
+     */
+    public String getCode() {
+        return this.code;
+    }
+
+    /**
+     * Sets code.
+     *
+     * @param code the code
+     */
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    /**
+     * Gets msg.
+     *
+     * @return the msg
+     */
+    public String getMsg() {
+        return msg;
+    }
+
+    /**
+     * Sets msg.
+     *
+     * @param msg the msg
+     */
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+
+    /**
+     * Gets error msg.
+     *
+     * @param code the code
+     * @return the error msg
+     */
+    public static String getErrorMsg(String code) {
+        Code[] errorCodes = values();
+        for (Code errCode : errorCodes) {
+            if (errCode.getCode().equals(code)) {
+                return errCode.getMsg();
+            }
+        }
+        return null;
+    }
+}
+

+ 1 - 1
src/main/java/cn/seecoder/fdroidrepository/utils/Result.java → src/main/java/cn/seecoder/fdroidrepository/result/Result.java

@@ -13,7 +13,7 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package cn.seecoder.fdroidrepository.utils;
+package cn.seecoder.fdroidrepository.result;
 
 import java.io.Serializable;
 

+ 63 - 0
src/main/java/cn/seecoder/fdroidrepository/result/SingleResult.java

@@ -0,0 +1,63 @@
+/*
+ *  Copyright 1999-2019 Seata.io Group.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package cn.seecoder.fdroidrepository.result;
+
+
+
+import java.io.Serializable;
+
+/**
+ * The single result
+ * @author zhongxiang.wang
+ */
+public class SingleResult<T> extends Result<T> implements Serializable {
+    private static final long serialVersionUID = 77612626624298767L;
+
+    /**
+     * the data
+     */
+    private T data;
+
+    public SingleResult(String code, String message) {
+        super(code, message);
+    }
+
+    public SingleResult(String code, String message, T data) {
+        super(code, message);
+        this.data = data;
+    }
+
+    public static <T> SingleResult<T> failure(String code, String msg) {
+        return new SingleResult<>(code, msg);
+    }
+
+    public static <T> SingleResult<T> failure(Code errorCode) {
+        return new SingleResult(errorCode.getCode(), errorCode.getMsg());
+    }
+
+    public static <T> SingleResult<T> success(T data) {
+        return new SingleResult<>(SUCCESS_CODE, SUCCESS_MSG,data);
+    }
+
+    public T getData() {
+        return data;
+    }
+
+    public void setData(T data) {
+        this.data = data;
+    }
+
+}

+ 25 - 0
src/main/java/cn/seecoder/fdroidrepository/security/JwtAuthenticationEntryPoint.java

@@ -0,0 +1,25 @@
+package cn.seecoder.fdroidrepository.security;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@Component
+public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);
+
+    @Override
+    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
+                         AuthenticationException e) throws IOException, ServletException {
+        LOGGER.error("Responding with unauthorized error. Message - {}", e.getMessage());
+        httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
+    }
+}

+ 83 - 0
src/main/java/cn/seecoder/fdroidrepository/security/JwtAuthenticationTokenFilter.java

@@ -0,0 +1,83 @@
+/*
+ *  Copyright 1999-2019 Seata.io Group.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package cn.seecoder.fdroidrepository.security;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+import cn.seecoder.fdroidrepository.config.WebSecurityConfig;
+import cn.seecoder.fdroidrepository.utils.JwtUtil;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+
+public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
+
+    private JwtUtil tokenProvider;
+
+    /**
+     * Instantiates a new Jwt authentication token filter.
+     *
+     * @param tokenProvider the token provider
+     */
+    public JwtAuthenticationTokenFilter(JwtUtil tokenProvider) {
+        this.tokenProvider = tokenProvider;
+    }
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
+        throws IOException, ServletException {
+        String jwt = resolveToken(request);
+
+        if (jwt != null && !"".equals(jwt.trim()) && SecurityContextHolder.getContext().getAuthentication() == null) {
+            if (this.tokenProvider.validateToken(jwt)) {
+                /**
+                 * get auth info
+                 */
+                Authentication authentication = this.tokenProvider.getAuthentication(jwt);
+                /**
+                 * save user info to securityContext
+                 */
+                SecurityContextHolder.getContext().setAuthentication(authentication);
+            }
+        }
+
+        chain.doFilter(request, response);
+    }
+
+    /**
+     * Get token from header
+     */
+    private String resolveToken(HttpServletRequest request) {
+        String bearerToken = request.getHeader(WebSecurityConfig.AUTHORIZATION_HEADER);
+        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(WebSecurityConfig.TOKEN_PREFIX)) {
+            return bearerToken.substring(WebSecurityConfig.TOKEN_PREFIX.length());
+        }
+        String jwt = request.getParameter(WebSecurityConfig.AUTHORIZATION_TOKEN);
+        if (StringUtils.hasText(jwt)) {
+            return jwt;
+        }
+        return null;
+    }
+}
+

+ 86 - 0
src/main/java/cn/seecoder/fdroidrepository/utils/JwtUtil.java

@@ -0,0 +1,86 @@
+package cn.seecoder.fdroidrepository.utils;
+
+import io.jsonwebtoken.*;
+import io.jsonwebtoken.io.Decoders;
+import io.jsonwebtoken.security.Keys;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.spec.SecretKeySpec;
+import java.security.Key;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+@Component
+public class JwtUtil {
+
+    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(JwtUtil.class);
+
+    @Value("${jwt.secret-key}")
+    private String SECRET_KEY;
+    private static final long EXPIRATION_TIME = 4 * 3600000; // 4小时过期
+
+
+    public String createToken(Authentication authentication) {
+        /**
+         * Current time
+         */
+        long now = (new Date()).getTime();
+        /**
+         * Expiration date
+         */
+        Date expirationDate = new Date(now + EXPIRATION_TIME);
+        /**
+         * Key
+         */
+        SecretKeySpec secretKeySpec = new SecretKeySpec(Decoders.BASE64.decode(SECRET_KEY),
+            SignatureAlgorithm.HS256.getJcaName());
+        /**
+         * create token
+         */
+
+        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
+        String authoritiesString = String.join(",", authorities.stream()
+            .map(GrantedAuthority::getAuthority)
+            .collect(Collectors.toList()));
+        return Jwts.builder().setSubject(authentication.getName()).claim("auth",authoritiesString).setExpiration(
+            expirationDate).signWith(secretKeySpec, SignatureAlgorithm.HS256).compact();
+    }
+
+    public boolean validateToken(String token) {
+        try {
+            Jwts.parserBuilder().setSigningKey(SECRET_KEY).build().parseClaimsJws(token);
+            return true;
+        } catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e) {
+            LOGGER.error("authorize jwt token failed!",e);
+            return false;
+        }
+    }
+
+    public Authentication getAuthentication(String token) {
+        /**
+         *  parse the payload of token
+         */
+        Claims claims = Jwts.parserBuilder().setSigningKey(SECRET_KEY).build().parseClaimsJws(token).getBody();
+        /**
+         * {
+         *   "auth": "ROLE_USER,ROLE_ADMIN",
+         * }
+         */
+        List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList((String) claims.get("auth"));
+
+        User principal = new User(claims.getSubject(), "", authorities);
+        return new UsernamePasswordAuthenticationToken(principal, "", authorities);
+    }
+
+
+}

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

@@ -12,6 +12,12 @@ mybatis.mapper-locations=classpath:mapper/*.xml
 
 community.path.domain=http://127.0.0.1:8080
 
+
+
+
+
+
+
 # 邮箱配置
 # SMTP服务器主机
 spring.mail.host=smtp.163.com
@@ -41,3 +47,9 @@ spring.mail.properties.mail.smtp.from=jiangjunminggggg@163.com
 spring.mail.default-encoding=UTF-8
 
 
+
+# 安全配置
+
+jwt.secret-key=abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg
+
+