4.0.0
com.mingjia
demo
0.0.1-SNAPSHOT
dataControl
data control content
org.springframework.boot
spring-boot-starter-parent
2.1.6.RELEASE
UTF-8
UTF-8
1.8
Edgware.SR1
2.2.0.RELEASE
2.8.5
4.2.1
1.3.0.Beta1
1.18.2
8.0.16
org.springframework.security.oauth
spring-security-oauth2
2.3.3.RELEASE
org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure
2.0.1.RELEASE
org.springframework.boot
spring-boot-starter-security
2.1.6.RELEASE
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-jpa
${spring-boot.version}
org.mapstruct
mapstruct-jdk8
${org.mapstruct.version}
org.mapstruct
mapstruct-processor
${org.mapstruct.version}
provided
org.springframework.security
spring-security-jwt
1.0.9.RELEASE
commons-io
commons-io
2.6
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.boot
spring-boot-starter-data-jpa
${spring-boot.version}
com.querydsl
querydsl-jpa
${querydsl.version}
io.springfox
springfox-swagger2
2.8.0
io.springfox
springfox-swagger-ui
2.8.0
com.google.code.gson
gson
${gson.version}
compile
com.jayway.jsonpath
json-path
org.projectlombok
lombok
${lombok.version}
provided
com.querydsl
querydsl-jpa
mysql
mysql-connector-java
${mysql-connector-java.version}
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-maven-plugin
com.mysema.maven
apt-maven-plugin
1.1.3
process
target/generated-sources/java
com.querydsl.apt.jpa.JPAAnnotationProcessor
com.querydsl
querydsl-apt
${querydsl.version}
src/main/resources
src/main/java
下面是oauth2相关配置,公4个class
package com.mingjia.data_control.config.oauth;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
@Configuration
public class AuthenticationBeanConfig {
@Autowired
private DataSource dataSource;
@Bean
@ConditionalOnMissingBean(PasswordEncoder.class)
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@ConditionalOnMissingBean(ClientDetailsService.class)
public ClientDetailsService clientDetails() {
return new JdbcClientDetailsService(dataSource);
}
}
package com.mingjia.data_control.config.oauth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.TokenStore;
@Configuration
@EnableAuthorizationServer
public class OAuth2ServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private TokenStore tokenStore;
@Autowired
private AccessTokenConverter accessTokenConverter;
@Autowired
private ClientDetailsService clientDetails;
// 配置令牌端点(Token Endpoint)的安全约束
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
// code授权添加
.realm("oauth2-resources")
// 开启/oauth/token_key验证端口无权限访问
.tokenKeyAccess("permitAll()")
// 接口/oauth/check_token允许检查令牌
.checkTokenAccess("isAuthenticated()")
// 使/oauth/token支持client_id以及client_secret作登录认证
.allowFormAuthenticationForClients()
// 密码编码器
.passwordEncoder(passwordEncoder);
}
// 配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
// 认证管理器
.authenticationManager(authenticationManager)
// 允许 GET、POST 请求获取 token,即访问端点:oauth/token
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
// 要使用refresh_token的话,需要额外配置userDetailsService
.userDetailsService(userDetailsService)
// 指定token存储位置
.tokenStore(tokenStore)
// 配置JwtAccessToken转换器
.accessTokenConverter(accessTokenConverter)
// 客户端详细信息服务的基本实现 这里使用JdbcClientDetailsService
.setClientDetailsService(clientDetails);
}
// 配置客户端详情服务
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 内存模式
/**
* clients.inMemory().withClient("demoApp").secret(bCryptPasswordEncoder.encode("demoAppSecret"))
.redirectUris("http://baidu.com")// code授权添加
.authorizedGrantTypes("authorization_code", "client_credentials", "password", "refresh_token")
// scopes的值就是all(全部权限),read,write等权限。就是第三方访问资源的一个权限,访问范围
.scopes("all")
// 这个资源服务的ID,这个属性是可选的,但是推荐设置并在授权服务中进行验证。
.resourceIds("oauth2-resource")
// 设置accessTokenValiditySeconds属性来设置Access Token的存活时间。
.accessTokenValiditySeconds(1200)
// 设置refreshTokenValiditySeconds属性来设置refresh Token的存活时间。
.refreshTokenValiditySeconds(50000);
*/
// 数据库模式
clients.withClientDetails(clientDetails); // 表中存储的secret值是加密后的值,并非明文;
}
}
package com.mingjia.data_control.config.oauth;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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;
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.GET,"/api/**").hasRole("ADMINISTRATOR")
.antMatchers("/oauth/confirm_access").permitAll()
.antMatchers("/**/*.js").permitAll()
.antMatchers("/favicon.ico").permitAll()
.and()
.requestMatchers().antMatchers("/api/**").and().authorizeRequests().antMatchers("/api/**").authenticated();
}
}
package com.mingjia.data_control.config.oauth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private RedisConnectionFactory connectionFactory;
@Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/oauth/**", "/login/**", "/logout/**")
.and()
.authorizeRequests()
.and()
.formLogin()
.permitAll()
.and()
.cors().disable();
}
@Bean
public TokenStore tokenStore() {
// 使用redis存储token信息
RedisTokenStore redisTokenStore = new RedisTokenStore(connectionFactory);
return redisTokenStore;
// 使用jwt内存存储token信息
// JwtTokenStore jwtTokenStore = new JwtTokenStore(accessTokenConverter());
// return jwtTokenStore;
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("healthy");
return converter;
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
return defaultTokenServices;
}
/**
* *需要配置这个支持password模式 support password grant type
*/
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
public static void main(String[] args) {
String encode = new BCryptPasswordEncoder().encode("dataApp");
String password = new BCryptPasswordEncoder().encode("123456");
System.out.println("encode:"+encode);
System.out.println("password"+password);
}
}
创建entity DataUser 要继承UserDetails
package com.mingjia.data_control.modules.base_module.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Transient;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 自定义UserDetails类 携带User实例
*/
@Getter
@Setter
@Entity
public class DataUser extends BaseModel implements UserDetails, Serializable {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
private String username;
private String password;
/**
* 是否激活
*/
@Transient
private Boolean active = true;
/**
* 角色
*/
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "data_user_role_map",
joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")})
private Set roles = new HashSet<>();
/**
* implements UserDetails
*/
@Override
public String getUsername() {
return username;
}
@JsonIgnore
@Override
public boolean isAccountNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isAccountNonLocked() {
return true;
}
@JsonIgnore
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@JsonIgnore
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return getGrantedAuthorities(this.getPermissions(roles));
}
private List getGrantedAuthorities(List getPermissions) {
List authorities = new ArrayList<>();
for (String permission : getPermissions) {
authorities.add(new SimpleGrantedAuthority(permission));
}
return authorities;
}
private List getPermissions(Collection roles) {
List permissions = new ArrayList<>();
for (DataRole role : roles) {
permissions.add("ROLE_" + role.getType().toString().toUpperCase());
for (DataPermission item : role.getPermissions()) {
permissions.add("OP_" +item.getCode().toUpperCase());
}
}
return permissions;
}
@Override
public boolean isEnabled() {
return active;
}
private static List mapToGrantedRoles(List authorities) {
return authorities.stream()
.map(authority -> new SimpleGrantedAuthority(authority.getName()))
.collect(Collectors.toList());
}
}
主要验证口:DataUserServiceImpl ,其他的IDataUserService我就不贴了
package com.mingjia.data_control.modules.base_module.service.impl;
import com.mingjia.data_control.modules.base_module.entity.DataPermission;
import com.mingjia.data_control.modules.base_module.entity.DataRole;
import com.mingjia.data_control.modules.base_module.entity.DataUser;
import com.mingjia.data_control.modules.base_module.service.IDataUserService;
import com.mingjia.data_control.modules.base_module.service.query.DataUserQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Component("userDetailsService")
public class DataUserServiceImpl extends BaseServiceImpl implements IDataUserService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional optionalDataUser = this.findOne(DataUserQuery.findByUserName(username));
DataUser dataUser = this.checkOptional(optionalDataUser);
/**
* isEnabled 账户是否启用
* isAccountNonExpired 账户没有过期
* isCredentialsNonExpired 身份认证是否是有效的
* isAccountNonLocked 账户没有被锁定
* 对于 isAccountNonLocked 和 isEnabled 没有做业务处理,只是抛出了对于的异常信息;
*/
List permissions = new ArrayList<>();
for (DataRole role : dataUser.getRoles()) {
permissions.add("ROLE_" + role.getType());
for (DataPermission item : role.getPermissions()) {
permissions.add("OP_" + item.getType() + "_" + item.getCode().toUpperCase());
}
}
String authorityString = String.join(",",permissions);
return new User(username, dataUser.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList(authorityString));
}
}
基本就完成了
application.properties配置
spring.application.name=oauth2-demo
server.port=8090
logging.level.root=info
###################################
# 数据库 MYSQL 设置
###################################
spring.datasource.url=jdbc:mysql://localhost/data_control?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# Hibernate ORM 设置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.main-server.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.main-server.datasource.initial-size=5
spring.main-server.datasource.max-active=20
spring.main-server.datasource.min-idle=5
spring.main-server.datasource.max-wait=60000
spring.main-server.datasource.validation-query=SELECT 1 FROM DUAL
spring.main-server.datasource.test-on-borrow=false
spring.main-server.datasource.test-on-return=false
spring.main-server.datasource.test-while-idle=true
spring.main-server.datasource.time-between-eviction-runs-millis=60000
spring.main-server.datasource.min-evictable-idle-time-millis=300000
spring.main-server.datasource.filter.stat.log-slow-sql=true
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=5000
数据库创建 - data_control
需要生成一个表oauth_client_details
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(48) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`resource_ids` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`client_secret` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`scope` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`authorized_grant_types` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`web_server_redirect_uri` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`authorities` varchar(256) 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` varchar(4096) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`autoapprove` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
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 ('demoApp',
'oauth2-resource', '$2a$10$z6DXhwPrzQe4wk9nGmqLvO6zQzYEAYscmNaAaDTDVhhuJGrhqZzk.',
'all', 'authorization_code,client_credentials,password,refresh_token',
'http://baidu.com', 'ROLE_CLIENT', 3600, 3600, NULL, NULL);
SET FOREIGN_KEY_CHECKS = 1;
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(48) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`resource_ids` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`client_secret` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`scope` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`authorized_grant_types` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`web_server_redirect_uri` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`authorities` varchar(256) 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` varchar(4096) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`autoapprove` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
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 ('dataApp',
'oauth2-resource', '$2a$10$8H0SUv/f0jU1KM2YNjYlouYGurGIWK2phCngtApaiucDFAeihxR5e',
'all', 'authorization_code,client_credentials,password,refresh_token',
'http://baidu.com', 'ROLE_CLIENT', 3600, 3600, NULL, NULL);
SET FOREIGN_KEY_CHECKS = 1;
找不到可以码云搜,实测可用
我用的是码云gitee
项目下载地址:
https://gitee.com/mingjia_xu/data_control/repository/archive/master.zip