初始化提交
This commit is contained in:
16
auth/authorization-server/.gitignore
vendored
Normal file
16
auth/authorization-server/.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
target/
|
||||
logs/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
114
auth/authorization-server/pom.xml
Normal file
114
auth/authorization-server/pom.xml
Normal file
@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>business.chaoran</groupId>
|
||||
<artifactId>authorization-server</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
<!-- <packaging>jar</packaging>-->
|
||||
|
||||
<name>authorization-server</name>
|
||||
<description>Demo Oauth2 project for Spring Cloud Oauth2 Authorization Server</description>
|
||||
|
||||
<parent>
|
||||
<groupId>business.chaoran</groupId>
|
||||
<artifactId>auth</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>business.chaoran</groupId>
|
||||
<artifactId>web</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!--oauth2认证-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.10.0.pr1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.9.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.9.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>2.9.0</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 独立运行依赖-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||
<!-- <artifactId>spring-boot-starter-web</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
|
||||
<!-- tomcat容器部署运行依赖-->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>2.1.4.RELEASE</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>build-info</goal>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<finalName>authorization-server</finalName>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,23 @@
|
||||
package com.springboot.auth.authorization;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableFeignClients
|
||||
public class Oauth2AuthorizationApplication extends SpringBootServletInitializer {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Oauth2AuthorizationApplication.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
|
||||
return builder.sources(Oauth2AuthorizationApplication.class);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,179 @@
|
||||
package com.springboot.auth.authorization.config;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.springboot.auth.authorization.exception.CustomWebResponseExceptionTranslator;
|
||||
import com.springboot.auth.authorization.oauth2.enhancer.CustomTokenEnhancer;
|
||||
import com.springboot.auth.authorization.oauth2.granter.MobileTokenGranter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
|
||||
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.CompositeTokenGranter;
|
||||
import org.springframework.security.oauth2.provider.TokenGranter;
|
||||
import org.springframework.security.oauth2.provider.approval.Approval;
|
||||
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
|
||||
import org.springframework.security.oauth2.provider.approval.InMemoryApprovalStore;
|
||||
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
|
||||
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
|
||||
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
|
||||
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.JwtTokenStore;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@EnableAuthorizationServer
|
||||
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("authenticationManagerBean")
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
// @Qualifier("dataSource")
|
||||
// @Autowired
|
||||
// DataSource dataSource;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("userDetailsService")
|
||||
UserDetailsService userDetailsService;
|
||||
|
||||
/**
|
||||
* jwt 对称加密密钥
|
||||
*/
|
||||
@Value("${spring.security.oauth2.jwt.signingKey}")
|
||||
private String signingKey;
|
||||
|
||||
@Override
|
||||
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
|
||||
// 支持将client参数放在header或body中
|
||||
oauthServer.allowFormAuthenticationForClients();
|
||||
|
||||
//认证策略,默认为denyAll()
|
||||
oauthServer.tokenKeyAccess("isAuthenticated()")
|
||||
.checkTokenAccess("permitAll()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
|
||||
// 配置客户端信息,从数据库中读取,对应oauth_client_details表
|
||||
// clients.jdbc(dataSource);
|
||||
clients.inMemory().withClient("test_client").secret("$2a$10$2szDKjvKHJCWE6YQNznogOeQF3USZHmCYj1fG7YbfK.vnTgNKLzri").scopes("read")
|
||||
.authorizedGrantTypes("client_credentials", "authorization_code", "mobile", "password", "refresh_token")
|
||||
.redirectUris("http://www.baidu.com").accessTokenValiditySeconds(3600).refreshTokenValiditySeconds(3600);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
|
||||
// 配置token的数据源、自定义的tokenServices等信息,配置身份认证器,配置认证方式,TokenStore,TokenGranter,OAuth2RequestFactory
|
||||
endpoints.tokenStore(tokenStore())
|
||||
.authorizationCodeServices(authorizationCodeServices())
|
||||
.approvalStore(approvalStore())
|
||||
.exceptionTranslator(customExceptionTranslator())
|
||||
.tokenEnhancer(tokenEnhancerChain())
|
||||
.authenticationManager(authenticationManager)
|
||||
.userDetailsService(userDetailsService)
|
||||
//update by joe_chen add granter
|
||||
.tokenGranter(tokenGranter(endpoints));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义OAuth2异常处理
|
||||
*
|
||||
* @return CustomWebResponseExceptionTranslator
|
||||
*/
|
||||
@Bean
|
||||
public WebResponseExceptionTranslator<OAuth2Exception> customExceptionTranslator() {
|
||||
return new CustomWebResponseExceptionTranslator();
|
||||
}
|
||||
|
||||
/**
|
||||
* 授权信息持久化实现
|
||||
* 密码模式无用
|
||||
* @return JdbcApprovalStore
|
||||
*/
|
||||
@Bean
|
||||
public ApprovalStore approvalStore() {
|
||||
InMemoryApprovalStore memoryApprovalStore = new InMemoryApprovalStore();
|
||||
Approval approval = new Approval("1", "test_client", "read", 3600, Approval.ApprovalStatus.APPROVED);
|
||||
memoryApprovalStore.addApprovals(Arrays.asList(approval));
|
||||
// return new JdbcApprovalStore(dataSource);
|
||||
return memoryApprovalStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* 授权码模式持久化授权码code
|
||||
* 密码模式无用
|
||||
* @return JdbcAuthorizationCodeServices
|
||||
*/
|
||||
@Bean
|
||||
protected AuthorizationCodeServices authorizationCodeServices() {
|
||||
// 授权码存储等处理方式类,使用jdbc,操作oauth_code表
|
||||
InMemoryAuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
|
||||
// return new JdbcAuthorizationCodeServices(dataSource);
|
||||
return authorizationCodeServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* token的持久化
|
||||
*
|
||||
* @return JwtTokenStore
|
||||
*/
|
||||
@Bean
|
||||
public TokenStore tokenStore() {
|
||||
return new JwtTokenStore(accessTokenConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义token
|
||||
*
|
||||
* @return tokenEnhancerChain
|
||||
*/
|
||||
@Bean
|
||||
public TokenEnhancerChain tokenEnhancerChain() {
|
||||
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
|
||||
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(new CustomTokenEnhancer(), accessTokenConverter()));
|
||||
return tokenEnhancerChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* jwt token的生成配置
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public JwtAccessTokenConverter accessTokenConverter() {
|
||||
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
|
||||
converter.setSigningKey(signingKey);
|
||||
return converter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置自定义的granter,手机号验证码登陆
|
||||
*
|
||||
* @param endpoints
|
||||
* @return
|
||||
*/
|
||||
public TokenGranter tokenGranter(final AuthorizationServerEndpointsConfigurer endpoints) {
|
||||
List<TokenGranter> granters = Lists.newArrayList(endpoints.getTokenGranter());
|
||||
granters.add(new MobileTokenGranter(
|
||||
authenticationManager,
|
||||
endpoints.getTokenServices(),
|
||||
endpoints.getClientDetailsService(),
|
||||
endpoints.getOAuth2RequestFactory()));
|
||||
return new CompositeTokenGranter(granters);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.springboot.auth.authorization.config;
|
||||
|
||||
/*
|
||||
**********************************************
|
||||
* DATE PERSON REASON
|
||||
* 2020/12/7 FXY Created
|
||||
**********************************************
|
||||
*/
|
||||
|
||||
import com.springboot.cloud.common.web.interceptor.FeignBasicAuthRequestInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class InterceptConfig {
|
||||
|
||||
@Bean
|
||||
public FeignBasicAuthRequestInterceptor interceptor(){
|
||||
return new FeignBasicAuthRequestInterceptor();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package com.springboot.auth.authorization.config;
|
||||
|
||||
/*
|
||||
**********************************************
|
||||
* DATE PERSON REASON
|
||||
* 2020-12-24 FXY Created
|
||||
**********************************************
|
||||
*/
|
||||
|
||||
import com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.Query;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.Set;
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class NacosRegisterConfig implements ApplicationRunner {
|
||||
|
||||
@Autowired(required = false)
|
||||
private NacosAutoServiceRegistration registration;
|
||||
|
||||
private Integer port;
|
||||
|
||||
public NacosRegisterConfig() {
|
||||
try {
|
||||
this.port = Integer.parseInt(getTomcatPort());
|
||||
} catch (Exception e) {
|
||||
log.error("获取tomcat端口出错了,原因:{}", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
if (registration != null && port != null) {
|
||||
registration.setPort(port);
|
||||
registration.start();
|
||||
}
|
||||
}
|
||||
|
||||
//获取tomcat端口
|
||||
private String getTomcatPort() throws Exception {
|
||||
MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
|
||||
Set<ObjectName> objectNames = beanServer.queryNames(new ObjectName("*:type=Connector,*"), Query.match(Query.attr("protocol"), Query.value("HTTP/1.1")));
|
||||
String port = objectNames.iterator().next().getKeyProperty("port");
|
||||
return port;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,83 @@
|
||||
package com.springboot.auth.authorization.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
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.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 com.springboot.auth.authorization.oauth2.granter.MobileAuthenticationProvider;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class WebServerSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("userDetailsService")
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("mobileUserDetailsService")
|
||||
private UserDetailsService mobileUserDetailsService;
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf().disable();
|
||||
http.authorizeRequests()
|
||||
.antMatchers("/actuator/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin().permitAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入自定义的userDetailsService实现,获取用户信息,设置密码加密方式
|
||||
*
|
||||
* @param authenticationManagerBuilder
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
|
||||
authenticationManagerBuilder
|
||||
.userDetailsService(userDetailsService)
|
||||
.passwordEncoder(passwordEncoder());
|
||||
// 设置手机验证码登陆的AuthenticationProvider
|
||||
authenticationManagerBuilder.authenticationProvider(mobileAuthenticationProvider());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 AuthenticationManager 注册为 bean , 方便配置 oauth server 的时候使用
|
||||
*/
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建手机验证码登陆的AuthenticationProvider
|
||||
*
|
||||
* @return mobileAuthenticationProvider
|
||||
*/
|
||||
@Bean
|
||||
public MobileAuthenticationProvider mobileAuthenticationProvider() {
|
||||
MobileAuthenticationProvider mobileAuthenticationProvider = new MobileAuthenticationProvider(this.mobileUserDetailsService);
|
||||
mobileAuthenticationProvider.setPasswordEncoder(passwordEncoder());
|
||||
return mobileAuthenticationProvider;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package com.springboot.auth.authorization.entity;
|
||||
|
||||
/*
|
||||
**********************************************
|
||||
* DATE PERSON REASON
|
||||
* 2020-12-29 FXY Created
|
||||
**********************************************
|
||||
*/
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class Company {
|
||||
private String companyId;
|
||||
private String userId;
|
||||
private String organizationId;
|
||||
private String empNo;
|
||||
private String position;
|
||||
private Integer isDefault;
|
||||
private Integer state;
|
||||
private String mobile;
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package com.springboot.auth.authorization.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class User {
|
||||
private String id;
|
||||
private String name;
|
||||
private Date registryTime;
|
||||
private String identity;
|
||||
private String nickName;
|
||||
private String photo;
|
||||
private String lastModifier;
|
||||
private Date lastModifyTime;
|
||||
private Double state;
|
||||
private String mobile;
|
||||
private String email;
|
||||
private String introduce;
|
||||
private String avatar;
|
||||
private String signature;
|
||||
private String password;
|
||||
private String username;
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package com.springboot.auth.authorization.exception;
|
||||
|
||||
import com.springboot.cloud.common.core.exception.ErrorType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum AuthErrorType implements ErrorType {
|
||||
|
||||
INVALID_REQUEST("040001", "无效请求"),
|
||||
INVALID_CLIENT("040002", "无效client_id"),
|
||||
INVALID_GRANT("040003", "无效授权"),
|
||||
INVALID_SCOPE("040004", "无效scope"),
|
||||
INVALID_TOKEN("040005", "无效token"),
|
||||
INSUFFICIENT_SCOPE("040010", "授权不足"),
|
||||
REDIRECT_URI_MISMATCH("040020", "redirect url不匹配"),
|
||||
ACCESS_DENIED("040030", "拒绝访问"),
|
||||
METHOD_NOT_ALLOWED("040040", "不支持该方法"),
|
||||
SERVER_ERROR("040050", "权限服务错误"),
|
||||
UNAUTHORIZED_CLIENT("040060", "未授权客户端"),
|
||||
UNAUTHORIZED("040061", "未授权"),
|
||||
UNSUPPORTED_RESPONSE_TYPE("040070", " 支持的响应类型"),
|
||||
UNSUPPORTED_GRANT_TYPE("040071", "不支持的授权类型");
|
||||
|
||||
/**
|
||||
* 错误类型码
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 错误类型描述信息
|
||||
*/
|
||||
private String mesg;
|
||||
|
||||
AuthErrorType(String code, String mesg) {
|
||||
this.code = code;
|
||||
this.mesg = mesg;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.springboot.auth.authorization.exception;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.springboot.cloud.common.core.entity.vo.Result;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@JsonSerialize(using = CustomOauthExceptionSerializer.class)
|
||||
class CustomOauthException extends OAuth2Exception {
|
||||
|
||||
private final Result result;
|
||||
|
||||
CustomOauthException(OAuth2Exception oAuth2Exception) {
|
||||
super(oAuth2Exception.getSummary(), oAuth2Exception);
|
||||
this.result = Result.fail(AuthErrorType.valueOf(oAuth2Exception.getOAuth2ErrorCode().toUpperCase()), oAuth2Exception);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.springboot.auth.authorization.exception;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class CustomOauthExceptionSerializer extends StdSerializer<CustomOauthException> {
|
||||
public CustomOauthExceptionSerializer() {
|
||||
super(CustomOauthException.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(CustomOauthException value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
gen.writeObject(value.getResult());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.springboot.auth.authorization.exception;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
|
||||
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
|
||||
|
||||
public class CustomWebResponseExceptionTranslator implements WebResponseExceptionTranslator<OAuth2Exception> {
|
||||
|
||||
@Override
|
||||
public ResponseEntity<OAuth2Exception> translate(Exception e) {
|
||||
|
||||
OAuth2Exception oAuth2Exception = (OAuth2Exception) e;
|
||||
return ResponseEntity.status(oAuth2Exception.getHttpErrorCode())
|
||||
.body(new CustomOauthException(oAuth2Exception));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package com.springboot.auth.authorization.exception;
|
||||
|
||||
import com.springboot.cloud.common.web.exception.DefaultGlobalExceptionHandlerAdvice;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandlerAdvice extends DefaultGlobalExceptionHandlerAdvice {
|
||||
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
package com.springboot.auth.authorization.oauth2;
|
||||
|
||||
import com.springboot.auth.authorization.entity.Company;
|
||||
import com.springboot.auth.authorization.entity.User;
|
||||
import com.springboot.auth.authorization.oauth2.authority.CustomGrantedAuthority;
|
||||
import com.springboot.auth.authorization.service.UserService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service("userDetailsService")
|
||||
public class CustomUserDetailsService implements UserDetailsService {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String payload) {
|
||||
|
||||
User user = userService.getUserByUsernameOrMobile(payload);
|
||||
log.info("load user by username :{}", user.toString());
|
||||
|
||||
return new org.springframework.security.core.userdetails.User(
|
||||
user.getUsername(),
|
||||
user.getPassword(), //加了密的密码
|
||||
user.getState() == 1, //账号是否可用
|
||||
user.getState() == 1, //账号未过期
|
||||
true, //密码未过期
|
||||
user.getState() == 1, //账户未锁定
|
||||
this.obtainGrantedCompany(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得登录者所有的公司信息
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected Set<CustomGrantedAuthority> obtainGrantedCompany(User user) {
|
||||
List<Company> companies = userService.getUserAllCompany(user.getId());
|
||||
return companies.stream().map(companyUser -> new CustomGrantedAuthority(companyUser.getCompanyId(), companyUser.getIsDefault() == 1)).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package com.springboot.auth.authorization.oauth2;
|
||||
|
||||
import com.springboot.auth.authorization.entity.User;
|
||||
import com.springboot.auth.authorization.provider.SmsCodeProvider;
|
||||
import com.springboot.auth.authorization.service.UserService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* 手机验证码登陆, 用户相关获取
|
||||
*/
|
||||
@Slf4j
|
||||
@Service("mobileUserDetailsService")
|
||||
public class MobileUserDetailsService extends CustomUserDetailsService {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
@Autowired
|
||||
private SmsCodeProvider smsCodeProvider;
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String payload) {
|
||||
User user = userService.getUserByUsernameOrMobile(payload);
|
||||
log.info("load user by mobile:{}", user.toString());
|
||||
|
||||
// 如果为mobile模式,从短信服务中获取验证码(动态密码)
|
||||
String credentials = smsCodeProvider.getSmsCode(payload, "LOGIN");
|
||||
|
||||
return new org.springframework.security.core.userdetails.User(
|
||||
user.getUsername(),
|
||||
credentials,
|
||||
user.getState() == 1 ? true : false,
|
||||
user.getState() == 1 ? true : false,
|
||||
user.getState() == 1 ? true : false,
|
||||
user.getState() == 1 ? true : false,
|
||||
super.obtainGrantedCompany(user));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package com.springboot.auth.authorization.oauth2.authority;
|
||||
|
||||
/*
|
||||
**********************************************
|
||||
* DATE PERSON REASON
|
||||
* 2021-01-05 FXY Created
|
||||
**********************************************
|
||||
*/
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* 根据getter方法进行序列化的
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class CustomGrantedAuthority implements GrantedAuthority {
|
||||
|
||||
private final String company;
|
||||
private boolean isDefault;
|
||||
|
||||
/**
|
||||
* 后期可换为角色编号
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return company;
|
||||
}
|
||||
|
||||
public boolean isDefault() {
|
||||
return isDefault;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.springboot.auth.authorization.oauth2.enhancer;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.springboot.auth.authorization.oauth2.authority.CustomGrantedAuthority;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CustomTokenEnhancer implements TokenEnhancer {
|
||||
|
||||
@Override
|
||||
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
|
||||
Map<String, Object> additionalInfo = Maps.newHashMap();
|
||||
additionalInfo.put("companies", authentication.getAuthorities());
|
||||
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
|
||||
return accessToken;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.springboot.auth.authorization.oauth2.granter;
|
||||
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
|
||||
public class MobileAuthenticationProvider extends DaoAuthenticationProvider {
|
||||
|
||||
public MobileAuthenticationProvider(UserDetailsService userDetailsService) {
|
||||
super.setUserDetailsService(userDetailsService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return MobileAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package com.springboot.auth.authorization.oauth2.granter;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
public class MobileAuthenticationToken extends UsernamePasswordAuthenticationToken {
|
||||
|
||||
public MobileAuthenticationToken(Authentication authenticationToken) {
|
||||
super(authenticationToken.getPrincipal(), authenticationToken.getCredentials());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package com.springboot.auth.authorization.oauth2.granter;
|
||||
|
||||
import org.springframework.security.authentication.*;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
|
||||
import org.springframework.security.oauth2.provider.*;
|
||||
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
|
||||
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MobileTokenGranter extends ResourceOwnerPasswordTokenGranter {
|
||||
|
||||
private static final String GRANT_TYPE = "mobile";
|
||||
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
public MobileTokenGranter(AuthenticationManager authenticationManager,
|
||||
AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
|
||||
OAuth2RequestFactory requestFactory) {
|
||||
super(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
|
||||
Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());
|
||||
String username = parameters.get("no");
|
||||
String password = parameters.get("password");
|
||||
// Protect from downstream leaks of password
|
||||
parameters.remove("no");
|
||||
|
||||
Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
|
||||
MobileAuthenticationToken mobileAuthenticationToken = new MobileAuthenticationToken(userAuth);
|
||||
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
|
||||
try {
|
||||
userAuth = this.authenticationManager.authenticate(mobileAuthenticationToken);
|
||||
} catch (AccountStatusException ase) {
|
||||
//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
|
||||
throw new InvalidGrantException(ase.getMessage());
|
||||
} catch (BadCredentialsException e) {
|
||||
// If the username/password are wrong the spec says we should send 400/invalid grant
|
||||
throw new InvalidGrantException(e.getMessage());
|
||||
}
|
||||
if (userAuth == null || !userAuth.isAuthenticated()) {
|
||||
throw new InvalidGrantException("Could not authenticate user: " + username);
|
||||
}
|
||||
|
||||
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
|
||||
return new OAuth2Authentication(storedOAuth2Request, mobileAuthenticationToken);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.springboot.auth.authorization.provider;
|
||||
|
||||
import com.springboot.auth.authorization.entity.Company;
|
||||
import com.springboot.auth.authorization.entity.User;
|
||||
import com.springboot.cloud.common.core.entity.vo.Result;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@FeignClient(name = "organization", fallback = OrganizationProviderFallback.class,path = "organization")
|
||||
//@FeignClient(name = "organization", fallback = OrganizationProviderFallback.class)
|
||||
public interface OrganizationProvider {
|
||||
|
||||
@GetMapping(value = "/user/getUserByUsernameOrMobile")
|
||||
Result<User> getUserByUsernameOrMobile(@RequestParam("payload")String payload);
|
||||
|
||||
@GetMapping(value = "/company/getUserAllCompany")
|
||||
Result<List<Company>> getUserAllCompany(@RequestParam("userId")String userId);
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.springboot.auth.authorization.provider;
|
||||
|
||||
import com.springboot.auth.authorization.entity.Company;
|
||||
import com.springboot.auth.authorization.entity.User;
|
||||
import com.springboot.cloud.common.core.entity.vo.Result;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class OrganizationProviderFallback implements OrganizationProvider {
|
||||
|
||||
@Override
|
||||
public Result<User> getUserByUsernameOrMobile(String payload) {
|
||||
return Result.success(new User());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<List<Company>> getUserAllCompany(String userId) {
|
||||
return Result.success(new HashSet<Company>());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.springboot.auth.authorization.provider;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
/**
|
||||
* todo 实现短信验证码的服务
|
||||
*/
|
||||
//@FeignClient(name = "sms", fallback = OrganizationProviderFallback.class,path="sms")
|
||||
public interface SmsCodeProvider {
|
||||
|
||||
/**
|
||||
* @param mobile
|
||||
* @return
|
||||
*/
|
||||
@GetMapping(value = "/sms/{mobile}")
|
||||
String getSmsCode(@PathVariable("mobile") String mobile, @RequestParam("businessType") String businessType);
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.springboot.auth.authorization.provider;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class SmsCodeProviderFallback implements SmsCodeProvider {
|
||||
|
||||
@Autowired
|
||||
PasswordEncoder passwordEncoder;
|
||||
|
||||
@Override
|
||||
public String getSmsCode(String mobile, String businessType) {
|
||||
// 该类为mock, 目前暂时没有sms的服务
|
||||
return passwordEncoder.encode("123456");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.springboot.auth.authorization.service;
|
||||
|
||||
import com.springboot.auth.authorization.entity.Company;
|
||||
import com.springboot.auth.authorization.entity.User;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface UserService {
|
||||
|
||||
/**
|
||||
* 通过账号或者手机号得到用户信息
|
||||
* @param payload 账号或者手机号
|
||||
* @return
|
||||
*/
|
||||
User getUserByUsernameOrMobile(String payload);
|
||||
|
||||
/**
|
||||
* 通过用户编号得到所有所属公司
|
||||
*/
|
||||
List<Company> getUserAllCompany(String userId);
|
||||
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package com.springboot.auth.authorization.service.impl;
|
||||
|
||||
import com.springboot.auth.authorization.entity.Company;
|
||||
import com.springboot.auth.authorization.entity.User;
|
||||
import com.springboot.auth.authorization.provider.OrganizationProvider;
|
||||
import com.springboot.auth.authorization.service.UserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
@Autowired
|
||||
private OrganizationProvider organizationProvider;
|
||||
|
||||
@Override
|
||||
public User getUserByUsernameOrMobile(String payload) {
|
||||
return organizationProvider.getUserByUsernameOrMobile(payload).getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Company> getUserAllCompany(String userId) {
|
||||
return organizationProvider.getUserAllCompany(userId).getData();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.springboot.auth.authorization;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
|
||||
public class ApplicationTests {
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
System.out.println(passwordEncoder.encode("test_secret"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user