- 导包
<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