springboot集成oauth2+security+redis的配置


  • 導包
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.6.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
  • 數據庫表配置
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details`  (
  `client_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `resource_ids` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `client_secret` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `scope` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `authorized_grant_types` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `web_server_redirect_uri` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `authorities` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `access_token_validity` int(11) NULL DEFAULT NULL,
  `refresh_token_validity` int(11) NULL DEFAULT NULL,
  `additional_information` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
  `create_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
  `archived` tinyint(1) NULL DEFAULT 0,
  `trusted` tinyint(1) NULL DEFAULT 0,
  `autoapprove` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT 'false',
  PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
INSERT INTO `oauth_client_details` VALUES ('user-clent', NULL, '$2a$10$DLARZJrTnLfUzK3XbVEuwuXJhBcr6t0ESsB9lf7Hg38x.kR0lzSwe', 'all', 'password,refresh_token', NULL, 'ROLE_WEB', NULL, NULL, '{}', '2021-12-09 09:32:41', 0, 0, 'true');

SET FOREIGN_KEY_CHECKS = 1;

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_sys_account
-- ----------------------------
DROP TABLE IF EXISTS `t_sys_account`;
CREATE TABLE `t_sys_account`  (
  `account_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NOT NULL COMMENT '用戶編號',
  `username` char(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '用戶名稱',
  `password` char(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '密度',
  `status` tinyint(2) NULL DEFAULT NULL COMMENT '狀態 1-正常',
  `active` tinyint(1) NULL DEFAULT NULL COMMENT '1-正常;0-禁用',
  PRIMARY KEY (`account_id`) USING BTREE,
  UNIQUE INDEX `uq_username`(`username`) USING BTREE COMMENT '用戶名唯一索引'
) ENGINE = InnoDB AUTO_INCREMENT = 10002 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用戶信息表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_sys_account
-- ----------------------------
INSERT INTO `t_sys_account` VALUES (10001, 1, 'zhangsan', '1122', 1, 1);

SET FOREIGN_KEY_CHECKS = 1;

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_sys_account_2_role
-- ----------------------------
DROP TABLE IF EXISTS `t_sys_account_2_role`;
CREATE TABLE `t_sys_account_2_role`  (
  `id` bigint(20) NOT NULL,
  `account_id` bigint(20) NOT NULL COMMENT '賬號ID',
  `role_id` bigint(20) NOT NULL COMMENT '角色ID',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '賬號角色映射表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_sys_account_2_role
-- ----------------------------
INSERT INTO `t_sys_account_2_role` VALUES (10001, 10001, 10001);

SET FOREIGN_KEY_CHECKS = 1;

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_sys_permission
-- ----------------------------
DROP TABLE IF EXISTS `t_sys_permission`;
CREATE TABLE `t_sys_permission`  (
  `perm_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `perm_code` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '資源代碼',
  `perm_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '資源名稱',
  `perm_url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '資源路徑',
  `perm_type` tinyint(2) NULL DEFAULT NULL COMMENT '授權范圍 是否鑒權。1-菜單;2-普通資源',
  `perm_desc` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '資源描述',
  PRIMARY KEY (`perm_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10002 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '系統權限資源' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_sys_permission
-- ----------------------------
INSERT INTO `t_sys_permission` VALUES (10001, 'USER_ADMIN', '用戶管理', '/user/**', 1, '用戶管理');

SET FOREIGN_KEY_CHECKS = 1;

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_sys_role
-- ----------------------------
DROP TABLE IF EXISTS `t_sys_role`;
CREATE TABLE `t_sys_role`  (
  `role_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色名稱',
  `role_code` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色代碼',
  `active` tinyint(1) NULL DEFAULT 1 COMMENT '是否啟用:1-啟用;0-禁用',
  `role_desc` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色描述',
  PRIMARY KEY (`role_id`) USING BTREE,
  INDEX `idx_rolecode`(`role_code`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10002 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '角色表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_sys_role
-- ----------------------------
INSERT INTO `t_sys_role` VALUES (10001, '用戶管理員', 'ROLE_ADMIN', 1, '系統管理員');

SET FOREIGN_KEY_CHECKS = 1;

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_sys_role_2_permission
-- ----------------------------
DROP TABLE IF EXISTS `t_sys_role_2_permission`;
CREATE TABLE `t_sys_role_2_permission`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `perm_id` bigint(20) NOT NULL COMMENT '資源ID',
  `role_id` bigint(20) NOT NULL COMMENT '角色ID',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_roleid_permid`(`role_id`, `perm_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10002 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '系統權限資源角色映射表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_sys_role_2_permission
-- ----------------------------
INSERT INTO `t_sys_role_2_permission` VALUES (10001, 10001, 10001);

SET FOREIGN_KEY_CHECKS = 1;
  • mapper 查詢

1、根據用戶名查詢用戶信息

    @Select({"SELECT t.account_id, t.username, t.`password`, t.active, t.`status`, t.user_id FROM t_sys_account t WHERE t.username=#{username} LIMIT 1"})
    SysUser getUserByName(@Param("username") String var1);

2、根據用戶id查詢用戶角色信息

    @Select({"SELECT t1.role_id, t1.role_code, t1.role_name FROM t_sys_account_2_role t LEFT JOIN t_sys_role t1 ON t.role_id=t1.role_id WHERE t.account_id=${accountId} AND t1.active=1"})
    List<SysRole> getRoleByUser(@Param("accountId") long var1);

3、根據角色id查詢權限信息

    @Select({"<script>SELECT p.perm_id, p.perm_code, p.perm_url  FROM t_sys_role_2_permission rp LEFT JOIN t_sys_permission p ON rp.perm_id = p.perm_id  WHERE rp.role_id IN <foreach collection=\"roleList\" item=\"r\" separator=\",\" open=\"(\" close=\")\">#{r}</foreach></script>"})
    List<SysPermission> listPermissionByRoleId(@Param("roleList") List<Long> var1);
  • security配置類-繼承 WebSecurityConfigurerAdapter 類
package com.zl.securitytest.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.annotation.Resource;

/**
 * spring security配置類
 *
 * @author z
 * @date 2021-12-01 16:06
 * @description:
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OauthWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

    @Resource
    private OauthUserDetailsService oauthUserDetailsService;

    @Resource
    private PasswordEncoder passwordEncoder;

    /**
     * 配置用戶身份驗證
     *
     * @param auth
     * @Author z
     * @Date 2021/12/1 17:09
     * @Return void
     * @Exception
     */
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(oauthUserDetailsService)
                .passwordEncoder(passwordEncoder);
    }

    /**
     * 注入 AuthenticationManager
     *
     * @param
     * @Author z
     * @Date 2021/12/7 14:50
     * @Return AuthenticationManager
     * @Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 配置攔截
     *
     * @param http
     * @Author z
     * @Date 2021/12/1 17:09
     * @Return void
     * @Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated();
        http.csrf().disable();
        http.logout().logoutUrl("/oauth/logout");
    }

}
  • 自定義用戶登錄驗證-實現 UserDetailsService 接口
package com.zl.securitytest.config;

import com.zl.securitytest.mapper.SysPermissionMapper;
import com.zl.securitytest.mapper.SysRoleUserMapper;
import com.zl.securitytest.mapper.SysUserMapper;
import com.zl.securitytest.pojo.SysPermission;
import com.zl.securitytest.pojo.SysRole;
import com.zl.securitytest.pojo.SysUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 自定義用戶登錄
 *
 * @author z
 * @date 2021-12-03 17:31
 */
@Service
@Slf4j
public class OauthUserDetailsService implements UserDetailsService {
    @Resource
    private SysUserMapper sysUserMapper;
    @Resource
    private SysRoleUserMapper sysRoleUserMapper;
    @Resource
    private SysPermissionMapper sysPermissionMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser user = this.sysUserMapper.getUserByName(username);
        if (null == user) {
            log.error("【{}】Account does not exist.", username);
            throw new UsernameNotFoundException(String.format("【%s】Account does not exist.", username));
        } else if (!user.getActive()) {
            log.error("【{}】The account is disabled.", username);
            throw new LockedException(String.format("【%s】The account is disabled.", username));
        } else if (!user.getStatus().equals(1)) {
            log.error("【{}】Account Status Is abnormal.", username);
            throw new LockedException(String.format("【%s】Account Status Is abnormal.", username));
        } else {
            user.setRoleList(this.getRoleList(user.getAccountId()));
            user.setAuthorities(this.listGrantedAuthority(user.getRoleList()));
            return user;
        }
    }

    // 獲取角色
    private List<SysRole> getRoleList(long accountId) {
        List<SysRole> roleList = this.sysRoleUserMapper.getRoleByUser(accountId);
        Iterator iter = roleList.iterator();

        while (iter.hasNext()) {
            SysRole role = (SysRole) iter.next();
            if (StringUtils.isEmpty(role.getRoleCode())) {
                iter.remove();
            }

            if (role.getRoleCode().indexOf("ROLE_") != 0) {
                iter.remove();
            }
        }

        return roleList;
    }

    // 獲取權限
    private List<GrantedAuthority> listGrantedAuthority(List<SysRole> roleList) {
        if (roleList.size() == 0) {
            return new ArrayList();
        } else {
            List<Long> roleIdList = (List) roleList.stream().map((a) -> {
                return a.getRoleId();
            }).collect(Collectors.toList());
            List<SysPermission> sysPermissions = this.sysPermissionMapper.listPermissionByRoleId(roleIdList);
            List<String> authorities = (List) sysPermissions.stream().map((a) -> {
                return a.getPermUrl();
            }).collect(Collectors.toList());
            roleList.forEach((a) -> {
                authorities.add(a.getRoleCode());
            });
            return AuthorityUtils.commaSeparatedStringToAuthorityList(String.join(",", authorities));
        }
    }
}
  • 自定義用戶類-實現 UserDetails 接口
package com.zl.securitytest.pojo;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

/**
 * 用戶信息類
 *
 * @author z
 * @date 2021-12-07 15:31
 */
@NoArgsConstructor
@Data
public class SysUser implements UserDetails, Serializable {
    /**
     * id
     */
    private Long userId;
    /**
     * 用戶id
     */
    private Long accountId;
    /**
     * 用戶名
     */
    private String username;
    /**
     * 用戶密碼
     */
    private String password;
    /**
     * 用戶狀態 1-正常
     */
    private Integer status;
    /**
     * 賬戶是否禁用 1-正常;0-禁用
     */
    private Boolean active;
    /**
     * 角色集合
     */
    private List<SysRole> roleList;
    /**
     * 權限集合
     */
    private List<GrantedAuthority> authorities;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public String getUsername() {
        return this.username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        if (this.status == null) {
            return false;
        } else {
            return this.status.equals(1);
        }
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return this.active;
    }
    @Override
    public String toString() {
        return "SysUser{" +
                "accountId=" + accountId+
                ", userId=" + userId +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", status=" + status +
                ", active=" + active +
                ", roleList=" + roleList +
                ", authorities=" + authorities +
                '}';
    }
}

實現UserDetails后注意方法里面返回屬性值(遇到棧溢出)

  • 驗證權限失敗處理-實現 AccessDeniedHandler 接口
package com.zl.securitytest.config;

import com.alibaba.fastjson.JSONObject;
import com.zl.securitytest.common.Result;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 驗證權限失敗處理類
 *
 * @author z
 * @date 2021-12-07 16:21
 */
@Component
public class SecurityAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();
        Result<Object> result = Result.failed(403, "沒有訪問權限");
        out.write(JSONObject.toJSONString(result));
        out.flush();
        out.close();
    }
}
  • oauth2 認證服務器配置-繼承 AuthorizationServerConfigurerAdapter 類
package com.zl.securitytest.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.TokenStore;

import javax.annotation.Resource;

/**
 * 配置認證服務器
 *
 * @author z
 * @date 2021-12-07 14:54
 */
@Configuration
@EnableAuthorizationServer
public class OauthAuthorizationServerConfigurerAdapter extends AuthorizationServerConfigurerAdapter {
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private UserDetailsService userDetailsService;
    @Resource
    private TokenStore tokenStore;
    @Resource
    private ApprovalStore approvalStore;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private JdbcClientDetailsService jdbcClientDetailsService;

    /**
     * 配置:安全檢查流程,用來配置令牌端點(Token Endpoint)的安全與權限訪問
     * 1. ClientDetail加密方式
     * 2. allowFormAuthenticationForClients 允許表單認證。針對/oauth/token端點。
     * 3. 添加開發配置tokenEndpointAuthenticationFilters
     * 4. tokenKeyAccess、checkTokenAccess訪問權限。
     * 默認過濾器:BasicAuthenticationFilter
     * 1、oauth_client_details表中clientSecret字段加密【ClientDetails屬性secret】
     * 2、CheckEndpoint類的接口 oauth/check_token 無需經過過濾器過濾,默認值:denyAll()
     * 對以下的幾個端點進行權限配置:
     * /oauth/authorize:授權端點
     * /oauth/token:令牌端點
     * /oauth/confirm_access:用戶確認授權提交端點
     * /oauth/error:授權服務錯誤信息端點
     * /oauth/check_token:用於資源服務訪問的令牌解析端點
     * /oauth/token_key:提供公有密匙的端點,如果使用JWT令牌的話
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 允許客戶端訪問oauth2授權接口,否則請求token返回401
        security.allowFormAuthenticationForClients()
                // 允許已授權的用戶訪問checkToken接口
                .checkTokenAccess("isAuthenticated()")
                // 允許已授權的用戶訪問獲取token接口
                .tokenKeyAccess("isAuthenticated()")
                .passwordEncoder(this.passwordEncoder);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//        BaseClientDetails baseClientDetails = new BaseClientDetails();
//        // 設置客戶端id
//        baseClientDetails.setClientId("my_client_id");
//        // 設置秘鑰
//        baseClientDetails.setClientSecret("my_test");
//        // 設置資源范圍
//        baseClientDetails.setScope(Arrays.asList(new String[]{"all"}));
//        // 設置授權類型
//        baseClientDetails.setAuthorizedGrantTypes(Arrays.asList(new String[]{"password", "refresh_token"}));
//        // 添加角色
//        baseClientDetails.setAuthorities(Arrays.asList(new GrantedAuthority[]{new SimpleGrantedAuthority("ROLE_WEB")}));
//        // 設置自動批准
//        baseClientDetails.setAutoApproveScopes(Arrays.asList(new String[]{"true"}));
//        // 添加客戶端配置
//        jdbcClientDetailsService.addClientDetails(baseClientDetails);

        // 配置客戶端
        clients.withClientDetails(this.jdbcClientDetailsService);
    }

    /**
     * 裝載類
     * 注入相關配置:
     * 1. 密碼模式下配置認證管理器 AuthenticationManager
     * 2. 設置 AccessToken的存儲介質tokenStore, 默認使用內存當做存儲介質。
     * 3. userDetailsService注入
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.approvalStore(this.approvalStore).authenticationManager(this.authenticationManager).userDetailsService(userDetailsService).tokenStore(tokenStore);
    }
}
  • oauth2 資源服務器配置-繼承 ResourceServerConfigurerAdapter 類
package com.zl.securitytest.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler;
import org.springframework.security.oauth2.provider.token.TokenStore;

import javax.annotation.Resource;

/**
 * 資源服務器
 *
 * @author z
 * @date 2021-12-07 16:29
 */
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(
        securedEnabled = true,
        prePostEnabled = true
)
public class OauthResourceServerConfigureAdapter extends ResourceServerConfigurerAdapter {
    @Resource
    private TokenStore tokenStore;
    @Resource
    private OAuth2WebSecurityExpressionHandler oAuth2WebSecurityExpressionHandler;
    @Resource
    private SecurityAccessDeniedHandler securityAccessDeniedHandler;

    /**
     * 資源安全配置
     *
     * @param resources
     * @throws Exception
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("order").tokenStore(this.tokenStore).expressionHandler(this.oAuth2WebSecurityExpressionHandler);
    }

    /***
     * http安全配置
     *
     * @param http
     * @Author z
     * @Date 2021/12/8 17:45
     * @Return void
     * @Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        /**
         * anyRequest----匹配所有請求路徑
         * access----springEl表達式結果為true時可以訪問
         * anonymous----匿名可以訪問
         * denyA1l----用戶不能訪問
         * fullyAuthenticated----用戶完全認證可以訪問(非remember-me下自動登錄)
         * hasAnyAuthority----如果有參效,參數表示權限,則其中任何一個權限可以訪問
         * hasAnyRole----如果有參數,參數表示角色,則其中任何一個角色可以訪問
         * hasAuthority----如果有參數,參數表示權限,則其權限可以訪問
         * hasIpAddress----如果有參數,參數表示IP她址,如果用戶IP和參效匹配,則可以訪問
         * hasRole----如果有參數,參數表示角色,則其角色可以訪問
         * permitAll----用戶可以任意訪問
         * rememberMe----允許通過remember-me登錄的用戶訪問
         * authenticated----用戶登錄后可訪問
         */
        http.exceptionHandling().accessDeniedHandler(this.securityAccessDeniedHandler);
        http.authorizeRequests().antMatchers(new String[]{"/doc.html", "/webjars/**", "/swagger-resources", "/v2/api-docs-ext", "/v2/api-docs"}).permitAll();
        http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, new String[]{"/**"}).permitAll();
        http.authorizeRequests().antMatchers(new String[]{"/oauth/token"}).permitAll();
        http.authorizeRequests().antMatchers(new String[]{"/oauth/**"}).authenticated();
        // 鑒定權限
        http.authorizeRequests().anyRequest().access("@oauthAuthorityControlService.isAuthority(request, authentication)");
        http.csrf().disable();
    }
}
  • 用戶權限對比
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.zl.securitytest.config;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.Iterator;

/**
 * 用戶權限對比
 *
 * @author z
 */
@Component("oauthAuthorityControlService")
public class OauthAuthorityControlService {
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    public OauthAuthorityControlService() {
    }

    public boolean isAuthority(HttpServletRequest request, Authentication authentication) {
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        String url = request.getRequestURI();
        Iterator var5 = authorities.iterator();

        GrantedAuthority authority;
        do {
            if (!var5.hasNext()) {
                return false;
            }

            authority = (GrantedAuthority) var5.next();
        } while (!this.antPathMatcher.match(authority.getAuthority(), url));

        return true;
    }
}
  • bean 資源配置
package com.zl.securitytest.config;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * 授權資源配置
 *
 * @author z
 * @date 2021-12-07 15:04
 */
@Configuration
public class AuthorizationResourceConfig {

    @Resource
    private RedisConnectionFactory redisConnectionFactory;
    @Resource
    private ApplicationContext applicationContext;

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        // 使用BCrypt加密密碼
        return new BCryptPasswordEncoder();
    }

    /**
     * token 存儲
     *
     * @return
     */
    @Bean
    public TokenStore tokenStore() {
        return new RedisTokenStore(this.redisConnectionFactory);
    }

    /**
     * 保存、檢索 user
     *
     * @return
     */
    @Bean
    public ApprovalStore approvalStore() {
        TokenApprovalStore tokenApprovalStore = new TokenApprovalStore();
        tokenApprovalStore.setTokenStore(this.tokenStore());
        return tokenApprovalStore;
    }

    /**
     * 從數據庫中讀取客戶端請求模式
     *
     * @return
     */
    @Bean
    public JdbcClientDetailsService jdbcClientDetailsService() {
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(this.applicationContext.getBean(DataSource.class));
        jdbcClientDetailsService.setPasswordEncoder(this.passwordEncoder());
        return jdbcClientDetailsService;
    }

    /**
     * web安全表達式處理器
     *
     * @return
     */
    @Bean
    public OAuth2WebSecurityExpressionHandler oAuth2WebSecurityExpressionHandler() {
        OAuth2WebSecurityExpressionHandler oAuth2WebSecurityExpressionHandler = new OAuth2WebSecurityExpressionHandler();
        oAuth2WebSecurityExpressionHandler.setApplicationContext(this.applicationContext);
        return oAuth2WebSecurityExpressionHandler;
    }
}
  • redis 配置
package com.zl.securitytest.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis的配置類
 *
 * @author z
 * @date 2021-12-06 10:42
 */
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}
management:
  health:
    redis:
      enabled: false # 不會根據redis的默認初始配置, localhost:6379 嘗試連接redis。
  • oauth2 請求令牌
http://localhost:8085/oauth/token?client_id=user-clent&client_secret=my_test&grant_type=password&username=zhangsan&password=1122

  • oauth2 刷新令牌
http://localhost:8085/oauth/token?client_id=user-clent&client_secret=my_test&grant_type=refresh_token&refresh_token=c10b0833-909d-4542-a845-7e22e3eb165e

  • oauth2 請求資源
http://localhost:8085/user/userInfo



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM