﻿{"id":1478,"date":"2023-08-10T10:50:18","date_gmt":"2023-08-10T02:50:18","guid":{"rendered":"http:\/\/blog.sway.com.cn\/?p=1478"},"modified":"2023-08-15T15:00:50","modified_gmt":"2023-08-15T07:00:50","slug":"springboot3-1-2security6-1-2jwtredis%e5%ae%9e%e7%8e%b0%e7%ae%80rbac%e6%9d%83%e9%99%90%e6%8e%a7%e5%88%b6","status":"publish","type":"post","link":"http:\/\/blog.sway.com.cn\/?p=1478","title":{"rendered":"SpringBoot3.1.2+Security6.1.2+Jwt+Redis\u5b9e\u73b0\u7b80RBAC\u6743\u9650\u63a7\u5236"},"content":{"rendered":"\n<p>\u7528JDK8\u7528\u4e86\u5dee\u4e0d\u591a\u5341\u5e74\u4e86\uff0c\u4e5f\u8be5\u66f4\u65b0\u4e00\u4e0b\u4e86\uff0c\u6bd5\u7adfSpringBoot3\u7cfb\u5217\u5df2\u7ecf\u4e0d\u518d\u652f\u6301JDK8\u4e86\uff0c\u6240\u4ee5\u672c\u6b21\u7684\u6574\u5408\u662f\u57fa\u4e8eJDK17\u7684\u3002<\/p>\n\n\n\n<p>SpringBoot3.1.2\u6574\u5408Security\u65f6\uff0cSecurity\u7684\u9ed8\u8ba4\u7248\u672c\u4e3a6.1.2\u3002<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p class=\"has-vivid-red-color has-text-color has-large-font-size\"><strong>\u5e9f\u8bdd\u4e0d\u591a\u8bf4\uff0c\u76f4\u63a5\u4e0a\u4ee3\u7801\u3002<\/strong><\/p>\n\n\n\n<p class=\"has-vivid-cyan-blue-color has-text-color\"><strong>\uff08response.write\u5c01\u88c5\u6211\u4e0d\u8d34\u4e86\uff0c\u8fd9\u79cd\u57fa\u7840\u4ee3\u7801\u81ea\u5df1\u89e3\u51b3\u5c31\u597d\uff09<\/strong><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>\u4f9d\u8d56\uff1a<strong>pom.xml\uff1a<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">        &lt;!--spring security \u542f\u52a8\u5668-->\n        &lt;dependency>\n            &lt;groupId>org.springframework.boot&lt;\/groupId>\n            &lt;artifactId>spring-boot-starter-security&lt;\/artifactId>\n            &lt;version>3.1.2&lt;\/version>\n        &lt;\/dependency>\n        &lt;!-- https:\/\/mvnrepository.com\/artifact\/javax.xml.bind\/jaxb-api -->\n        &lt;dependency>\n            &lt;groupId>javax.xml.bind&lt;\/groupId>\n            &lt;artifactId>jaxb-api&lt;\/artifactId>\n            &lt;version>2.3.1&lt;\/version>\n        &lt;\/dependency>\n        &lt;!--jwt-->\n        &lt;dependency>\n            &lt;groupId>io.jsonwebtoken&lt;\/groupId>\n            &lt;artifactId>jjwt&lt;\/artifactId>\n            &lt;version>0.9.0&lt;\/version>\n        &lt;\/dependency>\n        &lt;!-- mybatis -->\n        &lt;dependency>\n            &lt;groupId>org.mybatis.spring.boot&lt;\/groupId>\n            &lt;artifactId>mybatis-spring-boot-starter&lt;\/artifactId>\n            &lt;version>3.0.0&lt;\/version>\n        &lt;\/dependency>\n        &lt;!-- mysql -->\n        &lt;dependency>\n            &lt;groupId>com.mysql&lt;\/groupId>\n            &lt;artifactId>mysql-connector-j&lt;\/artifactId>\n            &lt;scope>runtime&lt;\/scope>\n        &lt;\/dependency>\n        &lt;!--redis-->\n        &lt;dependency>\n            &lt;groupId>org.springframework.boot&lt;\/groupId>\n            &lt;artifactId>spring-boot-starter-data-redis&lt;\/artifactId>\n        &lt;\/dependency><\/pre>\n\n\n\n<p>\u914d\u7f6e\u6587\u4ef6application.yml\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">server:\n  port: 8080\n\nspring:\n  # \u6570\u636e\u6e90\u914d\u7f6e\n  datasource:\n    url: jdbc:mysql:\/\/localhost:3306\/hkbea-gbt?useUnicode=true&amp;characterEncoding=UTF-8\n    username: root\n    password: 123456\n    driver-class-name: com.mysql.cj.jdbc.Driver\n\n\n# mybatis\u914d\u7f6e\nmybatis:\n  # \u914d\u7f6eSQL\u6620\u5c04\u6587\u4ef6\u8def\u5f84\n  mapper-locations: classpath:\/mapper\/*.xml\n  # \u5b9e\u4f53\u7c7b\u522b\u540d\u5305\u626b\u63cf\uff08\u8bf7\u66f4\u6362\u6210\u5b9e\u9645\u7684\uff09\n  type-aliases-package: **.**.**.**\n  # \u9a7c\u5cf0\u547d\u540d\n  configuration:\n    map-underscore-to-camel-case: true<\/pre>\n\n\n\n<p><strong>token\u5f02\u5e38\u5904\u7406\u7c7b\uff1aAuthenticationEntryPointImpl.java<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import com.fasterxml.jackson.databind.ObjectMapper;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.stereotype.Component;\n\nimport java.io.IOException;\n\n\/**\n * \u8ba4\u8bc1\u5931\u8d25\n *\/\n@Component\npublic class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {\n    @Override\n    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {\n        response.setContentType(\"application\/json;charset=UTF-8\");\n        response.getWriter().println(\"\u65e0\u6548\u767b\u5f55\u4fe1\u606f\");\n    }\n}<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\"><strong>permission\u5f02\u5e38\u5904\u7406\u7c7b\uff1aAccessDeniedHandlerImpl.java<\/strong><\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import com.fasterxml.jackson.databind.ObjectMapper;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.stereotype.Component;\n\nimport java.io.IOException;\n\n@Component\npublic class AccessDeniedHandlerImpl implements AccessDeniedHandler {\n    @Override\n    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {\n        response.setContentType(\"application\/json;charset=UTF-8\");\n        response.getWriter().println(\"\u4f60\u65e0\u6743\u8fdb\u884c\u672c\u64cd\u4f5c\");\n    }\n}<\/pre>\n\n\n\n<p>\u6838\u5fc3\u914d\u7f6e\u7c7b\uff1a<strong>SecurityConfig.java<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;\nimport org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.web.SecurityFilterChain;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\n\n\/**\n * SpringSecurity \u6838\u5fc3\u914d\u7f6e\u7c7b\n * prePostEnabled = true \u5f00\u542f\u6ce8\u89e3\u6743\u9650\u8ba4\u8bc1\u529f\u80fd\n *\/\n@Configuration\n@EnableWebSecurity\n@EnableMethodSecurity\npublic class SecurityConfig {\n\n    @Autowired\n    private AuthenticationConfiguration authenticationConfiguration;\n\n    \/\/\u8ba4\u8bc1\u8fc7\u6ee4\u5668\n    @Autowired\n    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;\n\n    \/\/ \u8ba4\u8bc1\u5931\u8d25\u5904\u7406\u5668\n    @Autowired\n    private AuthenticationEntryPointImpl authenticationEntryPoint;\n\n    \/\/ \u6388\u6743\u5931\u8d25\u5904\u7406\u5668\n    @Autowired\n    private AccessDeniedHandlerImpl accessDeniedHandler;\n\n    \/**\n     * \u8ba4\u8bc1\u914d\u7f6e\n     * anonymous()\uff1a\u533f\u540d\u8bbf\u95ee\uff1a\u672a\u767b\u5f55\u53ef\u4ee5\u8bbf\u95ee\uff0c\u5df2\u767b\u5f55\u4e0d\u80fd\u8bbf\u95ee\n     * permitAll()\uff1a\u6709\u6ca1\u6709\u8ba4\u8bc1\u90fd\u80fd\u8bbf\u95ee\uff1a\u767b\u5f55\u6216\u672a\u767b\u5f55\u90fd\u80fd\u8bbf\u95ee\n     * denyAll(): \u62d2\u7edd\n     * authenticated()\uff1a\u8ba4\u8bc1\u4e4b\u540e\u624d\u80fd\u8bbf\u95ee\n     * hasAuthority\uff08\uff09\uff1a\u5305\u542b\u6743\u9650\n     *\/\n    @Bean\n    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n\n        http\n            \/\/ \u5141\u8bb8\u8de8\u57df\uff08\u4e5f\u53ef\u4ee5\u4e0d\u5141\u8bb8\uff0c\u4e00\u822c\u524d\u540e\u7aef\u5206\u79bb\u9879\u76ee\u8981\u5173\u95ed\u6b64\u529f\u80fd\uff09\n            .cors()\n            \/\/\u5173\u95edcsrf\uff1a\u4e3a\u4e86\u9632\u6b62\u901a\u8fc7\u4f2a\u9020\u7528\u6237\u8bf7\u6c42\u6765\u8bbf\u95ee\u53d7\u4fe1\u7528\u7ad9\u70b9\u7684\u975e\u6cd5\u8bf7\u6c42\u8bbf\u95ee\n            .and()\n                .csrf().disable();\n        http\n            .anonymous().disable()  \/\/\u7981\u7528anonymous\u7528\u6237\uff08\u5f71\u54cdAuthentication.getPrincipal()\u7684\u8fd4\u56de\uff09\uff1adisable=>\u533f\u540d\u7528\u6237\u7684authentication\u4e3anull\n            \/\/ \u7981\u7528session (\u524d\u540e\u7aef\u5206\u79bb\u9879\u76ee\uff0c\u4e0d\u901a\u8fc7Session\u83b7\u53d6SecurityContext)\n            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)\n            \/\/\u767b\u5f55\u914d\u7f6e  \/\/\u6b64\u5904\u5168\u90e8\u6ce8\u91ca\uff0c\u5728\u672c\u6848\u4f8b\u4e2d\u91c7\u7528\u6ce8\u89e3\u8fdb\u884c\u6743\u9650\u62e6\u622a\n            \/\/.and()\n                \/\/ \u6dfb\u52a0token\u8fc7\u6ee4\u5668\n                \/\/.apply(permitAllSecurityConfig)\n                \/\/.and()\n                \/\/ \u914d\u7f6e\u8def\u5f84\u662f\u5426\u9700\u8981\u8ba4\u8bc1\n                \/\/.authorizeHttpRequests()\n                    \/\/\u914d\u7f6e\u653e\u884c\u8d44\u6e90\uff08\u6ce8\u610f: \u653e\u884c\u8d44\u6e90\u5fc5\u987b\u653e\u5728\u6240\u6709\u8ba4\u8bc1\u8bf7\u6c42\u4e4b\u524d!\uff09\n                    \/\/.requestMatchers(\"\/api\/v1\/auth\/login\").permitAll()\n                    \/\/.requestMatchers(\"\/api\/v1\/auth\/register\").permitAll()\n                    \/\/.requestMatchers(\"\/api\/v1\/on1on\/**\").permitAll()\n                    \/\/.requestMatchers(\"\/api\/v1\/admin\/**\").hasAuthority(\"admin\")  \/\/ \u5bf9\u4e8eadmin\u63a5\u53e3 \u9700\u8981\u6743\u9650admin\n                    \/\/ \u9664\u4e0a\u9762\u5916\u7684\u6240\u6709\u8bf7\u6c42\u5168\u90e8\u9700\u8981\u9274\u6743\u8ba4\u8bc1\n                    \/\/.anyRequest().authenticated()   \/\/\u4ee3\u8868\u6240\u6709\u8bf7\u6c42,\u5fc5\u987b\u8ba4\u8bc1\u4e4b\u540e\u624d\u80fd\u8bbf\u95ee\n                    \/\/.anyRequest().permitAll()\n            .and()\n                .authenticationManager(authenticationManager(authenticationConfiguration))\n                .sessionManagement()\n                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()\n                \/\/\u6b64\u5904\u4e3a\u6dfb\u52a0jwt\u8fc7\u6ee4\n                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)\n        ;\n\n        \/\/ \u914d\u7f6e\u5f02\u5e38\u5904\u7406\u5668\n        http.exceptionHandling()\n                \/\/ \u8ba4\u8bc1\u5931\u8d25\n                .authenticationEntryPoint(authenticationEntryPoint)\n                \/\/ \u6388\u6743\u5931\u8d25\n                .accessDeniedHandler(accessDeniedHandler);\n\n\n        http.headers().frameOptions().disable();\n\n        return http.build();\n    }\n\n    \/**\n     * \u6ce8\u5165AuthenticationManager \u8fdb\u884c\u7528\u6237\u8ba4\u8bc1(\u57fa\u4e8e\u7528\u6237\u540d\u548c\u5bc6\u7801\u6216\u4f7f\u7528\u7528\u6237\u540d\u548c\u5bc6\u7801\u8fdb\u884c\u8eab\u4efd\u9a8c\u8bc1)\n     * @param authenticationConfiguration\n     * @return\n     * @throws Exception\n     *\/\n    @Bean\n    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {\n        return authenticationConfiguration.getAuthenticationManager();\n    }\n\n    \/**\n     * \u63d0\u4f9b\u5bc6\u7801\u673a\u5bc6\u5904\u7406\u673a\u5236:\n     * \u5c06BCryptPasswordEncoder\u5bf9\u8c61\u6ce8\u5165\u5230spring\u5bb9\u5668\u4e2d,\u66f4\u6362\u6389\u539f\u6765\u7684 PasswordEncoder\u52a0\u5bc6\u65b9\u5f0f\n     * \u539fPasswordEncoder\u5bc6\u7801\u683c\u5f0f\u4e3a\uff1a{id}password\u3002\u5b83\u4f1a\u6839\u636eid\u53bb\u5224\u65ad\u5bc6\u7801\u7684\u52a0\u5bc6\u65b9\u5f0f\u3002\n     * \u5982\u679c\u6ca1\u66ff\u6362\u539f\u6765\u7684\u52a0\u5bc6\u65b9\u5f0f\uff0c\u6570\u636e\u5e93\u4e2d\u60f3\u7528\u660e\u6587\u5bc6\u7801\u505a\u6d4b\u8bd5\uff0c\u5c06\u5bc6\u7801\u5b57\u6bb5\u6539\u4e3a{noop}123456\u8fd9\u6837\u7684\u683c\u5f0f\n     *\/\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n        return new BCryptPasswordEncoder();\n    }\n\n}<\/pre>\n\n\n\n<p>RBAC\u4e2d\u7684Account.java\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import lombok.Data;\n\nimport java.io.Serializable;\n\n@Data\npublic class Account implements Serializable {\n\n    private String id;\n    private String username;\n    private String password;\n    private String nickname;\n\n}\n<\/pre>\n\n\n\n<p>RBAC\u4e2d\u7684AccountDao\uff1aAccountDao.java<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Select;\n\n@Mapper\npublic interface AccountDao {\n    \/**\n     * \u83b7\u53d6\u7528\u6237\u4fe1\u606f\n     *\/\n    @Select(\"select * from account where username = #{username}\")\n    Account getUserInfo(String username);\n}<\/pre>\n\n\n\n<p>RBAC\u4e2d\u7684AccountService\u63a5\u53e3\uff1aAccountService.java<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public interface AccountService {\n\n    \/**\n     * \u6839\u636e\u7528\u6237\u540d\u83b7\u53d6\u7528\u6237\u4fe1\u606f\n     *\/\n    Account getUserInfo(String username);\n}<\/pre>\n\n\n\n<p>RBAC\u4e2d\u7684AccountService\u63a5\u53e3\u5b9e\u73b0\u7c7b\uff1aAccountServiceImpl.java<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class AccountServiceImpl implements AccountService {\n\n    @Autowired\n    private AccountDao accountDao;\n\n    \/**\n     * \u6839\u636e\u7528\u6237\u540d\u83b7\u53d6\u7528\u6237\u4fe1\u606f\n     *\/\n    @Override\n    public Account getUserInfo(String username) {\n        return accountDao.getUserInfo(username);\n    }\n}<\/pre>\n\n\n\n<p>RBAC\u4e2d\u7684\u6743\u9650\u67e5\u8be2\u7c7b\uff1aAuthDao.java<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\nimport java.util.Map;\n\n@Mapper\npublic interface AuthDao {\n\n    List&lt;Map&lt;String,Object>> getRolesAndPermissionsByAccountId(String id);\n\n}\n<\/pre>\n\n\n\n<p>RBAC\u4e2d\u7684\u6743\u9650\u67e5\u8be2Mapper\uff1aAuthMapper.xml<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n&lt;!DOCTYPE mapper\n        PUBLIC \"-\/\/mybatis.org\/\/DTD Mapper 3.0\/\/EN\"\n        \"http:\/\/mybatis.org\/dtd\/mybatis-3-mapper.dtd\">\n&lt;mapper namespace=\"com.hkbea.gbt.dao.AuthDao\">\n\n    &lt;!--\u901a\u8fc7\u624b\u673a\u53f7\u67e5\u627e\u7528\u6237\u6743\u9650\u4fe1\u606f-->\n    &lt;select id=\"getRolesAndPermissionsByAccountId\" resultType=\"java.util.HashMap\" parameterType=\"String\">\n        SELECT role.name AS role, permission.name AS permission\n        FROM role,\n             permission,\n             account_role,\n             role_permission\n        WHERE account_role.account_id=#{id}\n          AND account_role.role_id=role.id\n          AND role_permission.role_id=role.id\n          AND role_permission.permission_id=permission.id\n    &lt;\/select>\n\n&lt;\/mapper><\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\"><strong>SpringSecurity\u6838\u5fc3\u7684\u7528\u6237\u7c7b\uff1aLoginUser.java<\/strong><\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import com.alibaba.fastjson2.annotation.JSONField;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.extern.log4j.Log4j2;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n\/**\n * \u91cd\u5199UserDetails\u8fd4\u56de\u7684\u7528\u6237\u4fe1\u606f\n * SpringSecurity\u8fd4\u56de\u7684\u7528\u6237\u4fe1\u606f\u5b9e\u4f53\u7c7b\n *\/\n@Log4j2\n@Data\n@NoArgsConstructor\npublic class LoginUser implements UserDetails {\n\n    private Account account;    \/\/\u7528\u6237\u4fe1\u606f\n\n    private List&lt;Map&lt;String,Object>> rolesAndPermissions; \/\/\u89d2\u8272\u4fe1\u606f\n\n    private List&lt;String> roles; \/\/\u89d2\u8272\u4fe1\u606f\n\n    private List&lt;String> permissions;   \/\/\u6743\u9650\u4fe1\u606f\n\n    public LoginUser(Account account, List&lt;Map&lt;String,Object>> rolesAndPermissions) {\n        this.account = account;\n        this.roles = new ArrayList&lt;>();\n        this.permissions = new ArrayList&lt;>();\n        for (Map&lt;String,Object> map : rolesAndPermissions){\n            log.info(map);\n            log.info(\"role : {}\",map.get(\"role\"));\n            this.roles.add((String)map.get(\"role\"));\n            log.info(\"permission : {}\",map.get(\"permission\"));\n            this.permissions.add((String)map.get(\"permission\"));\n        }\n    }\n\n    @JSONField(serialize = false) \/\/fastjson\u6ce8\u89e3,\u8868\u793a\u6b64\u5c5e\u6027\u4e0d\u4f1a\u88ab\u5e8f\u5217\u5316\uff0c\u56e0\u4e3aSimpleGrantedAuthority\u8fd9\u4e2a\u7c7b\u578b\u4e0d\u80fd\u5728redis\u4e2d\u5e8f\u5217\u5316\n    private List&lt;SimpleGrantedAuthority> authorities;\n\n    \/**\n     * \u83b7\u53d6\u6743\u9650\u4fe1\u606f\n     *\/\n    @Override\n    public Collection&lt;? extends GrantedAuthority> getAuthorities() {\n        \/\/ \u6743\u9650\u4e3a\u7a7a\u7684\u65f6\u5019\u624d\u5f80\u904d\u5386\uff0c\u4e0d\u4e3a\u7a7a\u76f4\u63a5\u8fd4\u56de\n        if (authorities != null) {\n            return authorities;\n        }\n        \/\/\u628apermissions\u4e2dString\u7c7b\u578b\u7684\u6743\u9650\u4fe1\u606f\u5c01\u88c5\u6210SimpleGrantedAuthority\u5bf9\u8c61\n        authorities = new ArrayList&lt;>();\n        for (String permission : permissions) {\n            SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permission);\n            authorities.add(authority);\n        }\n        return authorities;\n    }\n\n    \/**\n     * \u83b7\u53d6\u5bc6\u7801\n     *\/\n    @Override\n    public String getPassword() {\n        return account.getPassword();\n    }\n\n    \/**\n     * \u83b7\u53d6\u7528\u6237\u540d\n     *\/\n    @Override\n    public String getUsername() {\n        return account.getUsername();\n    }\n\n    \/**\n     * \u5224\u65ad\u662f\u5426\u8fc7\u671f(\u8d26\u6237\u662f\u5426\u672a\u8fc7\u671f\uff0c\u8fc7\u671f\u65e0\u6cd5\u9a8c\u8bc1)\n     *\/\n    @Override\n    public boolean isAccountNonExpired() {\n        return true;\n    }\n\n    \/**\n     * \u662f\u5426\u9501\u5b9a(\u6307\u5b9a\u7528\u6237\u662f\u5426\u89e3\u9501\uff0c\u9501\u5b9a\u7684\u7528\u6237\u65e0\u6cd5\u8fdb\u884c\u8eab\u4efd\u9a8c\u8bc1)\n     *\/\n    @Override\n    public boolean isAccountNonLocked() {\n        return true;\n    }\n\n    \/**\n     * \u662f\u5426\u6ca1\u6709\u8d85\u65f6(\u6307\u793a\u662f\u5426\u5df2\u8fc7\u671f\u7684\u7528\u6237\u7684\u51ed\u636e(\u5bc6\u7801)\uff0c\u8fc7\u671f\u7684\u51ed\u636e\u9632\u6b62\u8ba4\u8bc1)\n     *\/\n    @Override\n    public boolean isCredentialsNonExpired() {\n        return true;\n    }\n\n    \/**\n     * \u662f\u5426\u53ef\u7528(\u7528\u6237\u662f\u5426\u88ab\u542f\u7528\u6216\u7981\u7528\u3002\u7981\u7528\u7684\u7528\u6237\u65e0\u6cd5\u8fdb\u884c\u8eab\u4efd\u9a8c\u8bc1\u3002)\n     *\/\n    @Override\n    public boolean isEnabled() {\n        return true;\n    }\n\n}<\/pre>\n\n\n\n<p>Redis\u4f7f\u7528FastJson\u5e8f\u5217\u5316\u7684\u76f8\u5173\u7c7b\uff1aFastJsonRedisSerializer.java<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.parser.ParserConfig;\nimport com.alibaba.fastjson.serializer.SerializerFeature;\nimport com.fasterxml.jackson.databind.JavaType;\nimport com.fasterxml.jackson.databind.type.TypeFactory;\nimport org.springframework.data.redis.serializer.RedisSerializer;\nimport org.springframework.data.redis.serializer.SerializationException;\n\nimport java.nio.charset.Charset;\n\n\/**\n * Redis\u4f7f\u7528FastJson\u5e8f\u5217\u5316\n *\/\npublic class FastJsonRedisSerializer&lt;T> implements RedisSerializer&lt;T> {\n\n    public static final Charset DEFAULT_CHARSET = Charset.forName(\"UTF-8\");\n\n    private Class&lt;T> clazz;\n\n    static {\n        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);\n    }\n\n    public FastJsonRedisSerializer(Class&lt;T> clazz) {\n        super();\n        this.clazz = clazz;\n    }\n\n    @Override\n    public byte[] serialize(T t) throws SerializationException {\n        if (t == null) {\n            return new byte[0];\n        }\n        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);\n    }\n\n    @Override\n    public T deserialize(byte[] bytes) throws SerializationException {\n        if (bytes == null || bytes.length &lt;= 0) {\n            return null;\n        }\n        String str = new String(bytes, DEFAULT_CHARSET);\n\n        return JSON.parseObject(str, clazz);\n    }\n\n\n    protected JavaType getJavaType(Class&lt;?> clazz) {\n        return TypeFactory.defaultInstance().constructType(clazz);\n    }\n}<\/pre>\n\n\n\n<p>Redis\u914d\u7f6e\u7c7b\uff1aRedisConfig.java<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.serializer.StringRedisSerializer;\n\n\/**\n * redis\u914d\u7f6e\u7c7b\n * \u907f\u514d\u5b58\u5165redis\u4e2d\u7684key\u770b\u4e0a\u53bb\u4e71\u7801\u7684\u73b0\u8c61\n *\/\n@Configuration\npublic class RedisConfig {\n\n    @Bean\n    @SuppressWarnings(value = {\"unchecked\", \"rawtypes\"})\n    public RedisTemplate&lt;Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {\n        RedisTemplate&lt;Object, Object> template = new RedisTemplate&lt;>();\n        template.setConnectionFactory(connectionFactory);\n\n        \/\/\u4f7f\u7528FastJsonRedisSerializer\u6765\u5e8f\u5217\u5316\u548c\u53cd\u5e8f\u5217\u5316redis\u7684value\u503c\n        FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);\n\n        \/\/ \u4f7f\u7528StringRedisSerializer\u6765\u5e8f\u5217\u5316\u548c\u53cd\u5e8f\u5217\u5316redis\u7684key\u503c\n        template.setKeySerializer(new StringRedisSerializer());\n        template.setValueSerializer(serializer);\n\n        \/\/ Hash\u7684key\u4e5f\u91c7\u7528StringRedisSerializer\u7684\u5e8f\u5217\u5316\u65b9\u5f0f\n        template.setHashKeySerializer(new StringRedisSerializer());\n        template.setHashValueSerializer(serializer);\n\n        template.afterPropertiesSet();\n        return template;\n    }\n}<\/pre>\n\n\n\n<p>Redis\u5de5\u5177\u7c7b\uff1aRedisCache.java<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.redis.core.BoundSetOperations;\nimport org.springframework.data.redis.core.HashOperations;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.core.ValueOperations;\nimport org.springframework.stereotype.Component;\n\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\n\n\/**\n * redis\u5de5\u5177\u7c7b\n *\/\n@Component\npublic class RedisCache {\n    @Autowired\n    public RedisTemplate redisTemplate;\n\n    \/**\n     * \u7f13\u5b58\u57fa\u672c\u7684\u5bf9\u8c61\uff0cInteger\u3001String\u3001\u5b9e\u4f53\u7c7b\u7b49\n     *\n     * @param key   \u7f13\u5b58\u7684\u952e\u503c\n     * @param value \u7f13\u5b58\u7684\u503c\n     *\/\n    public &lt;T> void setCacheObject(final String key, final T value) {\n        redisTemplate.opsForValue().set(key, value);\n    }\n\n    \/**\n     * \u7f13\u5b58\u57fa\u672c\u7684\u5bf9\u8c61\uff0cInteger\u3001String\u3001\u5b9e\u4f53\u7c7b\u7b49\n     *\n     * @param key      \u7f13\u5b58\u7684\u952e\u503c\n     * @param value    \u7f13\u5b58\u7684\u503c\n     * @param timeout  \u65f6\u95f4\n     * @param timeUnit \u65f6\u95f4\u9897\u7c92\u5ea6\n     *\/\n    public &lt;T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {\n        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);\n    }\n\n    \/**\n     * \u8bbe\u7f6e\u6709\u6548\u65f6\u95f4\n     *\n     * @param key     Redis\u952e\n     * @param timeout \u8d85\u65f6\u65f6\u95f4\n     * @return true=\u8bbe\u7f6e\u6210\u529f\uff1bfalse=\u8bbe\u7f6e\u5931\u8d25\n     *\/\n    public boolean expire(final String key, final long timeout) {\n        return expire(key, timeout, TimeUnit.SECONDS);\n    }\n\n    \/**\n     * \u8bbe\u7f6e\u6709\u6548\u65f6\u95f4\n     *\n     * @param key     Redis\u952e\n     * @param timeout \u8d85\u65f6\u65f6\u95f4\n     * @param unit    \u65f6\u95f4\u5355\u4f4d\n     * @return true=\u8bbe\u7f6e\u6210\u529f\uff1bfalse=\u8bbe\u7f6e\u5931\u8d25\n     *\/\n    public boolean expire(final String key, final long timeout, final TimeUnit unit) {\n        return redisTemplate.expire(key, timeout, unit);\n    }\n\n    \/**\n     * \u83b7\u5f97\u7f13\u5b58\u7684\u57fa\u672c\u5bf9\u8c61\u3002\n     *\n     * @param key \u7f13\u5b58\u952e\u503c\n     * @return \u7f13\u5b58\u952e\u503c\u5bf9\u5e94\u7684\u6570\u636e\n     *\/\n    public &lt;T> T getCacheObject(final String key) {\n        ValueOperations&lt;String, T> operation = redisTemplate.opsForValue();\n        return operation.get(key);\n    }\n\n    \/**\n     * \u5220\u9664\u5355\u4e2a\u5bf9\u8c61\n     *\n     * @param key\n     *\/\n    public boolean deleteObject(final String key) {\n        return redisTemplate.delete(key);\n    }\n\n    \/**\n     * \u5220\u9664\u96c6\u5408\u5bf9\u8c61\n     *\n     * @param collection \u591a\u4e2a\u5bf9\u8c61\n     * @return\n     *\/\n    public long deleteObject(final Collection collection) {\n        return redisTemplate.delete(collection);\n    }\n\n    \/**\n     * \u7f13\u5b58List\u6570\u636e\n     *\n     * @param key      \u7f13\u5b58\u7684\u952e\u503c\n     * @param dataList \u5f85\u7f13\u5b58\u7684List\u6570\u636e\n     * @return \u7f13\u5b58\u7684\u5bf9\u8c61\n     *\/\n    public &lt;T> long setCacheList(final String key, final List&lt;T> dataList) {\n        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);\n        return count == null ? 0 : count;\n    }\n\n    \/**\n     * \u83b7\u5f97\u7f13\u5b58\u7684list\u5bf9\u8c61\n     *\n     * @param key \u7f13\u5b58\u7684\u952e\u503c\n     * @return \u7f13\u5b58\u952e\u503c\u5bf9\u5e94\u7684\u6570\u636e\n     *\/\n    public &lt;T> List&lt;T> getCacheList(final String key) {\n        return redisTemplate.opsForList().range(key, 0, -1);\n    }\n\n    \/**\n     * \u7f13\u5b58Set\n     *\n     * @param key     \u7f13\u5b58\u952e\u503c\n     * @param dataSet \u7f13\u5b58\u7684\u6570\u636e\n     * @return \u7f13\u5b58\u6570\u636e\u7684\u5bf9\u8c61\n     *\/\n    public &lt;T> BoundSetOperations&lt;String, T> setCacheSet(final String key, final Set&lt;T> dataSet) {\n        BoundSetOperations&lt;String, T> setOperation = redisTemplate.boundSetOps(key);\n        Iterator&lt;T> it = dataSet.iterator();\n        while (it.hasNext()) {\n            setOperation.add(it.next());\n        }\n        return setOperation;\n    }\n\n    \/**\n     * \u83b7\u5f97\u7f13\u5b58\u7684set\n     *\n     * @param key\n     * @return\n     *\/\n    public &lt;T> Set&lt;T> getCacheSet(final String key) {\n        return redisTemplate.opsForSet().members(key);\n    }\n\n    \/**\n     * \u7f13\u5b58Map\n     *\n     * @param key\n     * @param dataMap\n     *\/\n    public &lt;T> void setCacheMap(final String key, final Map&lt;String, T> dataMap) {\n        if (dataMap != null) {\n            redisTemplate.opsForHash().putAll(key, dataMap);\n        }\n    }\n\n    \/**\n     * \u83b7\u5f97\u7f13\u5b58\u7684Map\n     *\n     * @param key\n     * @return\n     *\/\n    public &lt;T> Map&lt;String, T> getCacheMap(final String key) {\n        return redisTemplate.opsForHash().entries(key);\n    }\n\n    \/**\n     * \u5f80Hash\u4e2d\u5b58\u5165\u6570\u636e\n     *\n     * @param key   Redis\u952e\n     * @param hKey  Hash\u952e\n     * @param value \u503c\n     *\/\n    public &lt;T> void setCacheMapValue(final String key, final String hKey, final T value) {\n        redisTemplate.opsForHash().put(key, hKey, value);\n    }\n\n    \/**\n     * \u83b7\u53d6Hash\u4e2d\u7684\u6570\u636e\n     *\n     * @param key  Redis\u952e\n     * @param hKey Hash\u952e\n     * @return Hash\u4e2d\u7684\u5bf9\u8c61\n     *\/\n    public &lt;T> T getCacheMapValue(final String key, final String hKey) {\n        HashOperations&lt;String, String, T> opsForHash = redisTemplate.opsForHash();\n        return opsForHash.get(key, hKey);\n    }\n\n    \/**\n     * \u5220\u9664Hash\u4e2d\u7684\u6570\u636e\n     *\n     * @param key\n     * @param hkey\n     *\/\n    public void delCacheMapValue(final String key, final String hkey) {\n        HashOperations hashOperations = redisTemplate.opsForHash();\n        hashOperations.delete(key, hkey);\n    }\n\n    \/**\n     * \u83b7\u53d6\u591a\u4e2aHash\u4e2d\u7684\u6570\u636e\n     *\n     * @param key   Redis\u952e\n     * @param hKeys Hash\u952e\u96c6\u5408\n     * @return Hash\u5bf9\u8c61\u96c6\u5408\n     *\/\n    public &lt;T> List&lt;T> getMultiCacheMapValue(final String key, final Collection&lt;Object> hKeys) {\n        return redisTemplate.opsForHash().multiGet(key, hKeys);\n    }\n\n    \/**\n     * \u83b7\u5f97\u7f13\u5b58\u7684\u57fa\u672c\u5bf9\u8c61\u5217\u8868\n     *\n     * @param pattern \u5b57\u7b26\u4e32\u524d\u7f00\n     * @return \u5bf9\u8c61\u5217\u8868\n     *\/\n    public Collection&lt;String> keys(final String pattern) {\n        return redisTemplate.keys(pattern);\n    }\n}<\/pre>\n\n\n\n<p>JWT\u5de5\u5177\u7c7b\uff1aJwtTokenUtil.java<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import io.jsonwebtoken.*;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\n@Configuration\n@Component\n@Slf4j\npublic class JwtTokenUtil {\n\n    \/**\n     * token\u7684\u5934key\n     *\/\n    public static final String AUTH_HEADER_KEY = \"Authorization\";\n    \/**\n     * token\u524d\u7f00\n     *\/\n    public static final String TOKEN_PREFIX = \"Bearer \";\n\n    \/**\n     * token \u8fc7\u671f\u65f6\u95f4 30\u5206\u949f\n     *\/\n    public static final long EXPIRATION = 1000 * 60 * 30;\n\n    \/**\n     * \u52a0\u5bc6\u7684key(\u81ea\u5df1\u751f\u6210\u7684\u4efb\u610f\u503c)\n     *\/\n    public static final String APP_SECRET_KEY = \"secret\";\n\n    \/**\n     * \u751f\u6210token\n     * @return token\u5b57\u7b26\u4e32\n     *\/\n    public static String createToken(Account account) {\n\n        Map&lt;String, Object> claims = new HashMap&lt;>();\n        claims.put(\"id\", account.getId());\n        claims.put(\"username\", account.getUsername());\n        claims.put(\"nickname\", account.getNickname());\n\n        String token = Jwts\n                .builder()\n                .setId(account.getId())\n                .setClaims(claims)\n                .setIssuedAt(new Date())\n                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))\n                .signWith(SignatureAlgorithm.HS256, APP_SECRET_KEY)\n                .compact();\n        return TOKEN_PREFIX +token;\n    }\n\n    \/**\n     * \u89e3\u6790jwt\n     * @return \u89e3\u6790\u540e\u7684JWT Claims\n     *\/\n    public static Claims parseJwt(String token) {\n        try {\n            return Jwts.parser().setSigningKey(APP_SECRET_KEY).parseClaimsJws(token.replace(TOKEN_PREFIX, \"\")).getBody();\n        } catch (Exception e) {\n            log.error(\"token exception : \", e);\n        }\n        return null;\n    }\n\n    \/**\n     * \u83b7\u53d6\u5f53\u524d\u767b\u5f55\u7528\u6237\u7684ID\n     *\n     * @param token\n     * @return\n     *\/\n    public static String getAccountId(String token) {\n        Claims claims = parseJwt(token);\n        return (String)claims.get(\"id\");\n    }\n\n    \/**\n     * \u83b7\u53d6\u5f53\u524d\u767b\u5f55\u7528\u6237\u7528\u6237\u540d\n     *\n     * @param token\n     * @return\n     *\/\n    public static String getUsername(String token) {\n        Claims claims = parseJwt(token);\n        return (String)claims.get(\"username\");\n    }\n\n    \/**\n     * \u68c0\u67e5token\u662f\u5426\u8fc7\u671f\n     *\n     * @param  token token\n     * @return boolean\n     *\/\n    public static boolean isExpiration(String token) {\n        Claims claims = parseJwt(token);\n        return claims.getExpiration().before(new Date());\n    }\n\n\n    public boolean validateToken(String token) {\n        try {\n            Jwts.parser().setSigningKey(APP_SECRET_KEY).parseClaimsJws(token);\n            return true;\n        } catch (SignatureException e) {\n            log.error(\"Invalid JWT signature: {}\", e.getMessage());\n        } catch (MalformedJwtException e) {\n            log.error(\"Invalid JWT token: {}\", e.getMessage());\n        } catch (ExpiredJwtException e) {\n            log.error(\"JWT token is expired: {}\", e.getMessage());\n        } catch (UnsupportedJwtException e) {\n            log.error(\"JWT token is unsupported: {}\", e.getMessage());\n        } catch (IllegalArgumentException e) {\n            log.error(\"JWT claims string is empty: {}\", e.getMessage());\n        }\n        return false;\n    }\n\n}\n\n<\/pre>\n\n\n\n<p>JWT\u6743\u9650\u62e6\u622a\u8fc7\u6ee4\u5668\uff1aJwtAuthenticationTokenFilter.java<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import com.alibaba.fastjson.JSONObject;\nimport io.jsonwebtoken.Claims;\nimport jakarta.annotation.Resource;\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\nimport lombok.extern.log4j.Log4j2;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport java.util.Objects;\n\n\/**\n * token\u8ba4\u8bc1\u8fc7\u6ee4\u5668\n * \u4f5c\u7528\uff1a\u89e3\u6790\u8bf7\u6c42\u5934\u4e2d\u7684token\u3002\u5e76\u9a8c\u8bc1\u5408\u6cd5\u6027\n * \u7ee7\u627f OncePerRequestFilter \u4fdd\u8bc1\u8bf7\u6c42\u7ecf\u8fc7\u8fc7\u6ee4\u5668\u4e00\u6b21\n *\/\n@Log4j2\n@Component\npublic class JwtAuthenticationTokenFilter extends OncePerRequestFilter {\n\n    @Resource\n    private RedisCache redisCache;\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain){\n\n        String token = request.getHeader(JwtTokenUtil.AUTH_HEADER_KEY);\n\n        if (StringUtils.hasText(token)) {\n            Claims claims = JwtTokenUtil.parseJwt(token);\n            if(claims!=null){\n                String accountId = (String)claims.get(\"id\");\n                String redisKey = \"login:\" + accountId;\n                \/\/ \u5148\u8f6c\u6210JSON\u5bf9\u8c61\n                JSONObject jsonObject = redisCache.getCacheObject(redisKey);\n                \/\/ JSON\u5bf9\u8c61\u8f6c\u6362\u6210Java\u5bf9\u8c61\n                LoginUser loginUser = jsonObject.toJavaObject(LoginUser.class);\n\n                \/\/ redis\u4e2d\u7528\u6237\u4e0d\u5b58\u5728\n                if (Objects.isNull(loginUser)) {\n                    throw new RuntimeException(\"redis\u4e2d\u7528\u6237\u4e0d\u5b58\u5728!\");\n                }\n\n                \/\/ \u5c06Authentication\u5bf9\u8c61\uff08\u7528\u6237\u4fe1\u606f\u3001\u5df2\u8ba4\u8bc1\u72b6\u6001\u3001\u6743\u9650\u4fe1\u606f\uff09\u5b58\u5165 SecurityContextHolder\n                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());\n                SecurityContextHolder.getContext().setAuthentication(authenticationToken);\n            }\n        }\n\n        log.info(\"authentication :{}\",SecurityContextHolder.getContext().getAuthentication());\n\n        \/\/\u653e\u884c\n        try {\n            filterChain.doFilter(request, response);    \/\/\u653e\u884c,\u56e0\u4e3a\u540e\u9762\u7684\u4f1a\u629b\u51fa\u76f8\u5e94\u7684\u5f02\u5e38\n        }catch (Exception e){\n            e.printStackTrace();\n        }\n\n    }\n}<\/pre>\n\n\n\n<p>\u9274\u6743Controller\u7c7b\uff1aAuthApiControllerV1.java<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import com.alibaba.fastjson2.JSONObject;\nimport com.hkbea.gbt.controller.api.v1.util.ApiControllerExceptionV1;\nimport com.hkbea.gbt.controller.api.v1.util.ResultVo;\nimport com.hkbea.gbt.model.Account;\nimport com.hkbea.gbt.on1on.On1onApiUtil;\nimport com.hkbea.gbt.security.core.LoginUser;\nimport com.hkbea.gbt.security.service.LoginService;\nimport com.hkbea.gbt.service.AccountService;\nimport jakarta.annotation.security.PermitAll;\nimport lombok.extern.log4j.Log4j2;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.UUID;\n\n@Log4j2\n@RestController\n@RequestMapping(\"\/api\/v1\/auth\")\npublic class AuthApiControllerV1 {\n\n    @Autowired\n    private LoginService loginService;\n\n    @Autowired\n    private AccountService accountService;\n\n    @PostMapping(\"login\")\n    @PermitAll\n    public ResultVo login(@RequestBody JSONObject body) throws ApiControllerExceptionV1 {\n\n        String username = body.getString(\"username\");\n        String password = body.getString(\"password\");\n\n        if(username==null||(username=username.trim()).equals(\"\")){\n            throw new ApiControllerExceptionV1(\"\u8bf7\u8f93\u5165\u6b63\u786e\u7684email\u8d26\u53f7\");\n        }\n        if(password==null||(password=password.trim()).equals(\"\")){\n            throw new ApiControllerExceptionV1(\"\u8bf7\u8f93\u5165password\");\n        }\n\n        String jwt = loginService.login(username,password);\n        if(StringUtils.isEmpty(jwt)){\n            throw new ApiControllerExceptionV1(\"\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef\");\n        }\n        return new ResultVo(ResultVo.SUCCESS,\"\u767b\u5f55\u6210\u529f\",jwt);\n    }\n\n    @GetMapping(\"info\")\n    @PreAuthorize(\"authenticated\")\n    public ResultVo info(Authentication authentication) throws ApiControllerExceptionV1 {\n\n        LoginUser loginUser = (LoginUser) authentication.getPrincipal();\n\n        Account account = loginUser.getAccount();\n\n        JSONObject out = new JSONObject();\n        out.put(\"id\",account.getId());\n        out.put(\"username\",account.getUsername());\n        out.put(\"on1on_id\",account.getOn1onId());\n\n        return new ResultVo(out);\n    }\n\n    \/**\n     * \u9000\u51fa\u767b\u5f55\n     *\/\n    @RequestMapping(value=\"logout\", method = {RequestMethod.DELETE})\n    @PreAuthorize(\"authenticated\")\n    public ResultVo logout() throws ApiControllerExceptionV1{\n        if(loginService.logout()){\n            return new ResultVo(ResultVo.SUCCESS,\"\u6ce8\u9500\u6210\u529f\");\n        }\n        throw new ApiControllerExceptionV1(\"\u9000\u51fa\u767b\u5f55\u5f02\u5e38\");\n    }\n}\n\n<\/pre>\n\n\n\n<p>\u6d4b\u8bd5Controller\uff1aTestApiControllerV1.java<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import com.alibaba.fastjson2.JSONObject;\nimport jakarta.annotation.security.PermitAll;\nimport lombok.extern.log4j.Log4j2;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.web.bind.annotation.*;\n\n@Log4j2\n@RestController\n@RequestMapping(\"\/api\/v1\/test\")\npublic class TestApiControllerV1 {\n\n    @GetMapping(\"\")\n    @PermitAll\n    public ResultVo test(){\n\n        log.info(\"test\");\n\n        JSONObject out = new JSONObject();\n\n        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();\n        if(authentication!=null){\n            LoginUser loginUser = (LoginUser) authentication.getPrincipal();\n            System.out.println(\"username :\"+loginUser.getAccount().getUsername());\n            System.out.println(\"\u51ed\u8bc1 :\"+authentication.getCredentials());\n            System.out.println(\"\u6743\u9650 :\"+authentication.getAuthorities());\n\n            out.put(\"username\",loginUser.getAccount().getUsername());\n            out.put(\"credentials\",authentication.getCredentials());\n            out.put(\"authorities\",authentication.getAuthorities());\n        }\n\n        return new ResultVo(out);\n    }\n\n}<\/pre>\n\n\n\n<p>MySQL\u6d4b\u8bd5\u6570\u636e\u811a\u672c\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">SET FOREIGN_KEY_CHECKS = 0;\n\n-- ----------------------------\n-- Table structure for account\n-- ----------------------------\nDROP TABLE IF EXISTS `account`;\nCREATE TABLE `account`  (\n  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,\n  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,\n  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,\n  `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Records of account\n-- ----------------------------\nINSERT INTO `account` VALUES ('5a07d8d95b9a46179d10a67f3765d363', 'admin', '$2a$10$MKzkFJJLuGMWO1eL9YuS9uinjq6H4Kalf9MzDxIFPQ4.fV9IKHNCC', '\u7ba1\u7406\u5458');\nINSERT INTO `account` VALUES ('85a223c1a519463e9e8de3a096818e6a', 'user', '123456', '\u666e\u901a\u7528\u6237');\n\n-- ----------------------------\n-- Table structure for account_role\n-- ----------------------------\nDROP TABLE IF EXISTS `account_role`;\nCREATE TABLE `account_role`  (\n  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,\n  `account_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,\n  `role_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Records of account_role\n-- ----------------------------\nINSERT INTO `account_role` VALUES ('eb014d68525849c29a5998e7133c1db9', '5a07d8d95b9a46179d10a67f3765d363', '28fb6f155b3b49e38a7241893b8d03f2');\n\n-- ----------------------------\n-- Table structure for permission\n-- ----------------------------\nDROP TABLE IF EXISTS `permission`;\nCREATE TABLE `permission`  (\n  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,\n  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Records of permission\n-- ----------------------------\nINSERT INTO `permission` VALUES ('80ad0fd1296f48a8bdd98ec700947c63', 'admin_console');\n\n-- ----------------------------\n-- Table structure for role\n-- ----------------------------\nDROP TABLE IF EXISTS `role`;\nCREATE TABLE `role`  (\n  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,\n  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Records of role\n-- ----------------------------\nINSERT INTO `role` VALUES ('28fb6f155b3b49e38a7241893b8d03f2', '\u7ba1\u7406\u5458');\n\n-- ----------------------------\n-- Table structure for role_permission\n-- ----------------------------\nDROP TABLE IF EXISTS `role_permission`;\nCREATE TABLE `role_permission`  (\n  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,\n  `role_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,\n  `permission_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Records of role_permission\n-- ----------------------------\nINSERT INTO `role_permission` VALUES ('28e1307e38124275b9e2d428adee3e0b', '28fb6f155b3b49e38a7241893b8d03f2', '80ad0fd1296f48a8bdd98ec700947c63');\n\nSET FOREIGN_KEY_CHECKS = 1;\n<\/pre>\n\n\n\n<p>\u6d4b\u8bd5\u7ed3\u679c\uff1a<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"http:\/\/blog.sway.com.cn\/wp-content\/uploads\/2023\/08\/image.png\"><img loading=\"lazy\" decoding=\"async\" width=\"961\" height=\"740\" src=\"http:\/\/blog.sway.com.cn\/wp-content\/uploads\/2023\/08\/image.png\" alt=\"\" class=\"wp-image-1480\" srcset=\"http:\/\/blog.sway.com.cn\/wp-content\/uploads\/2023\/08\/image.png 961w, http:\/\/blog.sway.com.cn\/wp-content\/uploads\/2023\/08\/image-300x231.png 300w, http:\/\/blog.sway.com.cn\/wp-content\/uploads\/2023\/08\/image-768x591.png 768w\" sizes=\"auto, (max-width: 961px) 100vw, 961px\" \/><\/a><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"http:\/\/blog.sway.com.cn\/wp-content\/uploads\/2023\/08\/image-1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"978\" height=\"791\" src=\"http:\/\/blog.sway.com.cn\/wp-content\/uploads\/2023\/08\/image-1.png\" alt=\"\" class=\"wp-image-1481\" srcset=\"http:\/\/blog.sway.com.cn\/wp-content\/uploads\/2023\/08\/image-1.png 978w, http:\/\/blog.sway.com.cn\/wp-content\/uploads\/2023\/08\/image-1-300x243.png 300w, http:\/\/blog.sway.com.cn\/wp-content\/uploads\/2023\/08\/image-1-768x621.png 768w\" sizes=\"auto, (max-width: 978px) 100vw, 978px\" \/><\/a><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"http:\/\/blog.sway.com.cn\/wp-content\/uploads\/2023\/08\/image-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"663\" src=\"http:\/\/blog.sway.com.cn\/wp-content\/uploads\/2023\/08\/image-2-1024x663.png\" alt=\"\" class=\"wp-image-1482\" srcset=\"http:\/\/blog.sway.com.cn\/wp-content\/uploads\/2023\/08\/image-2-1024x663.png 1024w, http:\/\/blog.sway.com.cn\/wp-content\/uploads\/2023\/08\/image-2-300x194.png 300w, http:\/\/blog.sway.com.cn\/wp-content\/uploads\/2023\/08\/image-2-768x497.png 768w, http:\/\/blog.sway.com.cn\/wp-content\/uploads\/2023\/08\/image-2.png 1059w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>\u7528JDK8\u7528\u4e86\u5dee\u4e0d\u591a\u5341\u5e74\u4e86\uff0c\u4e5f\u8be5\u66f4\u65b0\u4e00\u4e0b\u4e86\uff0c\u6bd5\u7adfSpringBoot3\u7cfb\u5217\u5df2\u7ecf\u4e0d &hellip; <a href=\"http:\/\/blog.sway.com.cn\/?p=1478\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1478","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"http:\/\/blog.sway.com.cn\/index.php?rest_route=\/wp\/v2\/posts\/1478","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/blog.sway.com.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blog.sway.com.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blog.sway.com.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/blog.sway.com.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1478"}],"version-history":[{"count":5,"href":"http:\/\/blog.sway.com.cn\/index.php?rest_route=\/wp\/v2\/posts\/1478\/revisions"}],"predecessor-version":[{"id":1486,"href":"http:\/\/blog.sway.com.cn\/index.php?rest_route=\/wp\/v2\/posts\/1478\/revisions\/1486"}],"wp:attachment":[{"href":"http:\/\/blog.sway.com.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1478"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blog.sway.com.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1478"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blog.sway.com.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1478"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}