update 2021年11月3日18:23:35
parent
5830c7873b
commit
3faa725ec1
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.zhangmeng.api.service.oauth;
|
||||||
|
|
||||||
|
import com.zhangmeng.model.entity.User;
|
||||||
|
import com.zhangmeng.model.vo.Result;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
|
||||||
|
@Api(tags = "用户授权管理")
|
||||||
|
public interface AuthControllerApi {
|
||||||
|
|
||||||
|
@ApiOperation("令牌解析")
|
||||||
|
public User parseToken();
|
||||||
|
|
||||||
|
@ApiOperation("用户授权")
|
||||||
|
public Result login(String username, String password);
|
||||||
|
}
|
||||||
|
|
@ -89,5 +89,15 @@
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
<artifactId>fastjson</artifactId>
|
<artifactId>fastjson</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.zhangmeng.model.entity;
|
||||||
|
|
||||||
|
import com.zhangmeng.model.base.baseEntity.BaseEntity;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhengmeng
|
||||||
|
* @version 1.0
|
||||||
|
* @date 2021年1月7日17:44:40
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Entity
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Table(name = "oauth_config")
|
||||||
|
public class OauthConfig extends BaseEntity<Long> {
|
||||||
|
|
||||||
|
private Integer ttl;
|
||||||
|
|
||||||
|
private String clientId;
|
||||||
|
|
||||||
|
private String clientSecret;
|
||||||
|
|
||||||
|
private String cookieDomain;
|
||||||
|
|
||||||
|
private Integer cookieMaxAge;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
<?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">
|
||||||
|
<parent>
|
||||||
|
<artifactId>mystyle-cloud-parent</artifactId>
|
||||||
|
<groupId>com.zhangmeng</groupId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>mystyle-cloud-oauth</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-sleuth</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-data</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-oauth2</artifactId>
|
||||||
|
<version>2.2.4.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.zhangmeng</groupId>
|
||||||
|
<artifactId>mystyle-cloud-api</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.zhangmeng.oauth;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||||
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhangmeng
|
||||||
|
* @date 2021年11月3日17:00:00
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableDiscoveryClient
|
||||||
|
@EnableFeignClients
|
||||||
|
public class OauthApplication {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(OauthApplication.class,args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
package com.zhangmeng.oauth.config;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.cloud.bootstrap.encrypt.KeyProperties;
|
||||||
|
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.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
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.client.JdbcClientDetailsService;
|
||||||
|
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
|
||||||
|
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 org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 转身的背影在心底里沉沦
|
||||||
|
* @date 2021年9月10日09:46:38
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableAuthorizationServer
|
||||||
|
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
|
||||||
|
|
||||||
|
//数据源,用于从数据库获取数据进行认证操作,测试可以从内存中获取
|
||||||
|
@Autowired
|
||||||
|
private DataSource dataSource;
|
||||||
|
|
||||||
|
//jwt令牌转换器
|
||||||
|
@Autowired
|
||||||
|
private JwtAccessTokenConverter jwtAccessTokenConverter;
|
||||||
|
|
||||||
|
//SpringSecurity 用户自定义授权认证类
|
||||||
|
@Qualifier("userDetailsServiceImpl")
|
||||||
|
@Autowired
|
||||||
|
UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
//授权认证管理器
|
||||||
|
@Autowired
|
||||||
|
AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
|
//令牌持久化存储接口
|
||||||
|
@Autowired
|
||||||
|
TokenStore tokenStore;
|
||||||
|
|
||||||
|
@Resource(name = "keyProp")
|
||||||
|
private KeyProperties keyProperties;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 客户端信息配置
|
||||||
|
* @param clients
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
|
||||||
|
// 动态获取客户端信息
|
||||||
|
clients.jdbc(dataSource) // 加载数据源
|
||||||
|
.clients(clientDetails()); // 查询客户端数据
|
||||||
|
// clients.inMemory() //基于内存
|
||||||
|
// .withClient("mystyle-cloud") //客户端id
|
||||||
|
// .secret("mystyle-cloud") //秘钥
|
||||||
|
// .redirectUris("http://localhost") //重定向地址
|
||||||
|
// .accessTokenValiditySeconds(60) //访问令牌有效期
|
||||||
|
// .refreshTokenValiditySeconds(60) //刷新令牌有效期
|
||||||
|
// .authorizedGrantTypes(
|
||||||
|
// "authorization_code", //根据授权码生成令牌
|
||||||
|
// "client_credentials", //客户端认证
|
||||||
|
// "refresh_token", //刷新令牌
|
||||||
|
// "password") //密码方式认证
|
||||||
|
// .scopes("app"); //客户端范围,名称自定义,必填
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 授权服务器端点配置
|
||||||
|
* @param endpoints
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
|
||||||
|
endpoints.accessTokenConverter(jwtAccessTokenConverter)
|
||||||
|
.authenticationManager(authenticationManager)//认证管理器
|
||||||
|
.tokenStore(tokenStore) //令牌存储
|
||||||
|
.userDetailsService(userDetailsService); //用户信息service
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 授权服务器的安全配置
|
||||||
|
* @param oauthServer
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
|
||||||
|
oauthServer.allowFormAuthenticationForClients()
|
||||||
|
.passwordEncoder(new BCryptPasswordEncoder())
|
||||||
|
.tokenKeyAccess("permitAll()")
|
||||||
|
.checkTokenAccess("isAuthenticated()");
|
||||||
|
}
|
||||||
|
|
||||||
|
//读取密钥的配置
|
||||||
|
@Bean("keyProp")
|
||||||
|
public KeyProperties keyProperties() {
|
||||||
|
return new KeyProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//客户端配置
|
||||||
|
@Bean
|
||||||
|
public ClientDetailsService clientDetails() {
|
||||||
|
return new JdbcClientDetailsService(dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Autowired
|
||||||
|
public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
|
||||||
|
return new JwtTokenStore(jwtAccessTokenConverter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****
|
||||||
|
* JWT令牌转换器
|
||||||
|
* @param customUserAuthenticationConverter
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public JwtAccessTokenConverter jwtAccessTokenConverter(CustomUserAuthenticationConverter customUserAuthenticationConverter) {
|
||||||
|
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
|
||||||
|
KeyPair keyPair = new KeyStoreKeyFactory(
|
||||||
|
keyProperties.getKeyStore().getLocation(), //证书路径
|
||||||
|
keyProperties.getKeyStore().getSecret().toCharArray()) //证书秘钥
|
||||||
|
.getKeyPair(
|
||||||
|
keyProperties.getKeyStore().getAlias(), //证书别名
|
||||||
|
keyProperties.getKeyStore().getPassword().toCharArray()); //证书密码
|
||||||
|
converter.setKeyPair(keyPair);
|
||||||
|
//配置自定义的CustomUserAuthenticationConverter
|
||||||
|
DefaultAccessTokenConverter accessTokenConverter = (DefaultAccessTokenConverter) converter.getAccessTokenConverter();
|
||||||
|
accessTokenConverter.setUserTokenConverter(customUserAuthenticationConverter);
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.zhangmeng.oauth.config;
|
||||||
|
|
||||||
|
import com.zhangmeng.oauth.utils.UserJwt;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
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.oauth2.provider.token.DefaultUserAuthenticationConverter;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义用户授权转换
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class CustomUserAuthenticationConverter extends DefaultUserAuthenticationConverter {
|
||||||
|
|
||||||
|
@Qualifier("userDetailsServiceImpl")
|
||||||
|
@Autowired
|
||||||
|
UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, ?> convertUserAuthentication(Authentication authentication) {
|
||||||
|
HashMap<String,Object> response = new LinkedHashMap<>();
|
||||||
|
String name = authentication.getName();
|
||||||
|
response.put("username", name);
|
||||||
|
|
||||||
|
Object principal = authentication.getPrincipal();
|
||||||
|
UserJwt userJwt = null;
|
||||||
|
if (principal instanceof UserJwt) {
|
||||||
|
userJwt = (UserJwt) principal;
|
||||||
|
} else {
|
||||||
|
//refresh_token默认不去调用userdetailService获取用户信息,这里我们手动去调用,得到 UserJwt
|
||||||
|
UserDetails userDetails = userDetailsService.loadUserByUsername(name);
|
||||||
|
userJwt = (UserJwt) userDetails;
|
||||||
|
}
|
||||||
|
response.put("name", userJwt.getName());
|
||||||
|
response.put("id", userJwt.getId());
|
||||||
|
if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
|
||||||
|
response.put("authorities", AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.zhangmeng.oauth.config;
|
||||||
|
|
||||||
|
import com.zhangmeng.oauth.utils.JwtToken;
|
||||||
|
import feign.RequestInterceptor;
|
||||||
|
import feign.RequestTemplate;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求拦截器
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class FeignOauth2RequestInterceptor implements RequestInterceptor {
|
||||||
|
@Override
|
||||||
|
public void apply(RequestTemplate requestTemplate) {
|
||||||
|
// 获取的全部请求信息
|
||||||
|
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||||
|
if (attributes != null){
|
||||||
|
HttpServletRequest request = attributes.getRequest();
|
||||||
|
// 获取所有的请求头信息
|
||||||
|
Enumeration<String> headerNames = request.getHeaderNames();
|
||||||
|
if (headerNames != null){
|
||||||
|
while (headerNames.hasMoreElements()){
|
||||||
|
// 获取请求头的key
|
||||||
|
String element = headerNames.nextElement();
|
||||||
|
// 获取请求头的value
|
||||||
|
String value = request.getHeader(element);
|
||||||
|
// 将请求头信息放入到请求头
|
||||||
|
requestTemplate.header(element,value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 获得带有权限的令牌(将令牌放入请求头中,用于登录到的时候携带权限信息)
|
||||||
|
String token = JwtToken.adminJwt();
|
||||||
|
// 如果微服务之间相互调用,也需要将令牌放入请求头中,
|
||||||
|
requestTemplate.header("Authorization","bearer " + token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.zhangmeng.oauth.config;
|
||||||
|
|
||||||
|
import com.zhangmeng.domain.admin.Permission;
|
||||||
|
import com.zhangmeng.feign.admin.AdminFeign;
|
||||||
|
import com.zhangmeng.oauth.utils.UserJwt;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.User;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.oauth2.provider.ClientDetails;
|
||||||
|
import org.springframework.security.oauth2.provider.ClientDetailsService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义授权认证类
|
||||||
|
*
|
||||||
|
* @author 转身的背影在心底里沉沦
|
||||||
|
* @date 2021年9月14日11:54:013
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class UserDetailsServiceImpl implements UserDetailsService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ClientDetailsService clientDetailsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AdminFeign adminFeign;
|
||||||
|
|
||||||
|
/****
|
||||||
|
* 自定义授权认证
|
||||||
|
* @param username
|
||||||
|
* @return
|
||||||
|
* @throws UsernameNotFoundException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
//取出身份,如果身份为空说明没有认证
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
//没有认证统一采用httpbasic认证,httpbasic中存储了client_id和client_secret,开始认证client_id和client_secret
|
||||||
|
if(authentication==null){
|
||||||
|
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(username);
|
||||||
|
if(clientDetails!=null){
|
||||||
|
//秘钥
|
||||||
|
String clientSecret = clientDetails.getClientSecret();
|
||||||
|
//静态方式
|
||||||
|
//return new User(username,new BCryptPasswordEncoder().encode(clientSecret), AuthorityUtils.commaSeparatedStringToAuthorityList(""));
|
||||||
|
//数据库查找方式
|
||||||
|
return new User(username,clientSecret, AuthorityUtils.commaSeparatedStringToAuthorityList(""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isEmpty(username)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过数据库去查询用户通过密码授权
|
||||||
|
com.zhangmeng.domain.admin.User user = this.adminFeign.findByUserName(username);
|
||||||
|
|
||||||
|
//根据用户查询权限列表
|
||||||
|
List<Permission> permissions = this.adminFeign.findByUserId(user.getId());
|
||||||
|
Set<SimpleGrantedAuthority> collect = permissions.parallelStream().filter(permission -> !org.springframework.util.StringUtils.isEmpty(permission.getTitle()))
|
||||||
|
.map(permission -> new SimpleGrantedAuthority(permission.getTitle())).collect(Collectors.toSet());
|
||||||
|
return new UserJwt(username,user.getPassword(),collect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
package com.zhangmeng.oauth.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Order(-1)
|
||||||
|
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 采用BCryptPasswordEncoder对密码进行编码
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 忽略安全拦截的URL
|
||||||
|
* @param web
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void configure(WebSecurity web) throws Exception {
|
||||||
|
// 对静态资源放行
|
||||||
|
web.ignoring().antMatchers(
|
||||||
|
"/user/login",
|
||||||
|
"/user/logout",
|
||||||
|
"/oauth/login"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跨域支持
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public WebMvcConfigurer corsConfigurer() {
|
||||||
|
return new WebMvcConfigurer() {
|
||||||
|
@Override
|
||||||
|
public void addCorsMappings(CorsRegistry registry) {
|
||||||
|
registry.addMapping("/**").allowedMethods("*");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 创建授权管理认证对象
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@Override
|
||||||
|
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||||
|
return super.authenticationManagerBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/****
|
||||||
|
*
|
||||||
|
* @param http
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void configure(HttpSecurity http) throws Exception {
|
||||||
|
|
||||||
|
http.csrf().disable()
|
||||||
|
.httpBasic() //启用Http基本身份验证
|
||||||
|
.and()
|
||||||
|
.formLogin() //启用表单身份验证
|
||||||
|
// 自定义登录请求(视图请求)
|
||||||
|
.loginPage("/oauth/login")
|
||||||
|
// springSecurity自带登录地址
|
||||||
|
.loginProcessingUrl("/user/login")
|
||||||
|
.and()
|
||||||
|
.authorizeRequests() //限制基于Request请求访问
|
||||||
|
.anyRequest()
|
||||||
|
.authenticated(); //其他请求都需要经过验证
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
package com.zhangmeng.oauth.controller;
|
||||||
|
|
||||||
|
import com.alibaba.cloud.commons.lang.StringUtils;
|
||||||
|
import com.zhangmeng.api.service.oauth.AuthControllerApi;
|
||||||
|
import com.zhangmeng.model.base.baseController.BaseController;
|
||||||
|
import com.zhangmeng.model.entity.OauthConfig;
|
||||||
|
import com.zhangmeng.model.entity.User;
|
||||||
|
import com.zhangmeng.model.vo.Result;
|
||||||
|
import com.zhangmeng.model.vo.StatusCode;
|
||||||
|
import com.zhangmeng.oauth.dto.AuthToken;
|
||||||
|
import com.zhangmeng.oauth.feign.AdminFeign;
|
||||||
|
import com.zhangmeng.oauth.service.AuthService;
|
||||||
|
import com.zhangmeng.oauth.service.OauthConfigService;
|
||||||
|
import com.zhangmeng.oauth.utils.CookieUtil;
|
||||||
|
import com.zhangmeng.oauth.utils.TokenTools;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(value = "/user")
|
||||||
|
public class AuthController extends BaseController implements AuthControllerApi {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AdminFeign adminFeign;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OauthConfigService oauthConfigService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthService authService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TokenTools tokenTools;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@GetMapping("/oauth/parseToken")//解析
|
||||||
|
public User parseToken() {
|
||||||
|
Map<String, String> userInfo = this.tokenTools.getUserInfo();
|
||||||
|
String username = userInfo.get("username");
|
||||||
|
return this.adminFeign.findByUserName(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PostMapping("/login")
|
||||||
|
public Result login(String username, String password) {
|
||||||
|
if (StringUtils.isEmpty(username)) {
|
||||||
|
throw new RuntimeException("用户名不允许为空");
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(password)) {
|
||||||
|
throw new RuntimeException("密码不允许为空");
|
||||||
|
}
|
||||||
|
User user = this.adminFeign.findByUserName(username);
|
||||||
|
if (user == null){
|
||||||
|
throw new UsernameNotFoundException("用户名错误");
|
||||||
|
}
|
||||||
|
//校验密码是否正确,如果正确则申请令牌
|
||||||
|
if (!this.passwordEncoder.matches(password,user.getPassword())) {
|
||||||
|
throw new RuntimeException("密码错误");
|
||||||
|
}
|
||||||
|
OauthConfig oauthConfig = this.oauthConfig();
|
||||||
|
AuthToken authToken = auth_login(username, password,oauthConfig);
|
||||||
|
//用户身份令牌
|
||||||
|
String access_token = authToken.getAccessToken();
|
||||||
|
//将令牌存储到cookie
|
||||||
|
saveCookie(access_token,oauthConfig);
|
||||||
|
return new Result(true, StatusCode.OK, "登录成功!",authToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AuthToken auth_login(String username, String password,OauthConfig oauthConfig){
|
||||||
|
//客户端ID
|
||||||
|
String clientId = oauthConfig.getClientId();
|
||||||
|
//秘钥
|
||||||
|
String clientSecret = oauthConfig.getClientSecret();
|
||||||
|
//申请令牌
|
||||||
|
return this.authService.login(username, password, clientId, clientSecret);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OauthConfig oauthConfig(){
|
||||||
|
OauthConfig oauthConfig = this.oauthConfigService.oauthConfig();
|
||||||
|
if (oauthConfig == null) {
|
||||||
|
throw new RuntimeException("oauthConfig 配置读取错误");
|
||||||
|
}
|
||||||
|
return oauthConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 将令牌存储到cookie
|
||||||
|
* @param token
|
||||||
|
*/
|
||||||
|
private void saveCookie(String token,OauthConfig oauthConfig) {
|
||||||
|
//Cookie存储的域名
|
||||||
|
String cookieDomain = oauthConfig.getCookieDomain();
|
||||||
|
//Cookie生命周期
|
||||||
|
int cookieMaxAge = oauthConfig.getCookieMaxAge();
|
||||||
|
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
|
||||||
|
CookieUtil.addCookie(response, cookieDomain, "/", "Authorization", token, cookieMaxAge, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.zhangmeng.oauth.dao;
|
||||||
|
|
||||||
|
|
||||||
|
import com.zhangmeng.model.base.baseDao.AbstractBaseMapper;
|
||||||
|
import com.zhangmeng.model.entity.OauthConfig;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface OauthConfigDao extends AbstractBaseMapper<OauthConfig> {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.zhangmeng.oauth.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AuthToken implements Serializable {
|
||||||
|
|
||||||
|
//令牌信息
|
||||||
|
String accessToken;
|
||||||
|
|
||||||
|
//刷新token(refresh_token)
|
||||||
|
String refreshToken;
|
||||||
|
|
||||||
|
//jwt短令牌
|
||||||
|
String jti;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.zhangmeng.oauth.dto;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.jwt.Jwt;
|
||||||
|
import org.springframework.security.jwt.JwtHelper;
|
||||||
|
import org.springframework.security.jwt.crypto.sign.RsaSigner;
|
||||||
|
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
|
||||||
|
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 转身的背影在心底里沉沦
|
||||||
|
* @date 2021年9月18日12:23:40
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
public class JwtToken {
|
||||||
|
public static String adminJwt(){
|
||||||
|
//证书文件路径
|
||||||
|
String key_location="mystyle-cloud.jks";
|
||||||
|
//秘钥库密码
|
||||||
|
String key_password="mystyle-cloud";
|
||||||
|
//秘钥密码
|
||||||
|
String keypwd = "mystyle-cloud";
|
||||||
|
//秘钥别名
|
||||||
|
String alias = "mystyle-cloud";
|
||||||
|
|
||||||
|
//访问证书路径
|
||||||
|
ClassPathResource resource = new ClassPathResource(key_location);
|
||||||
|
|
||||||
|
//创建秘钥工厂
|
||||||
|
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource,key_password.toCharArray());
|
||||||
|
|
||||||
|
//读取秘钥对(公钥、私钥)
|
||||||
|
KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias,keypwd.toCharArray());
|
||||||
|
|
||||||
|
//获取私钥
|
||||||
|
RSAPrivateKey rsaPrivate = (RSAPrivateKey) keyPair.getPrivate();
|
||||||
|
|
||||||
|
//定义Payload9(载荷,给生成的令牌加权限)
|
||||||
|
Map<String, Object> tokenMap = new HashMap<>();
|
||||||
|
// key 就是解析后的authorities, value是权限 信息可以有多个
|
||||||
|
tokenMap.put("authorities", new String[]{"admin"});
|
||||||
|
|
||||||
|
//生成Jwt令牌
|
||||||
|
Jwt jwt = JwtHelper.encode(JSON.toJSONString(tokenMap), new RsaSigner(rsaPrivate));
|
||||||
|
|
||||||
|
//取出令牌
|
||||||
|
return jwt.getEncoded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.zhangmeng.oauth.dto;
|
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.User;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class UserJwt extends User {
|
||||||
|
private String id; //用户ID
|
||||||
|
private String name; //用户名字
|
||||||
|
|
||||||
|
public UserJwt(String username, String password, Collection<? extends GrantedAuthority> authorities) {
|
||||||
|
super(username, password, authorities);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.zhangmeng.oauth.feign;
|
||||||
|
|
||||||
|
import com.zhangmeng.model.entity.User;
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
|
@FeignClient("")
|
||||||
|
public interface AdminFeign {
|
||||||
|
|
||||||
|
@RequestMapping("")
|
||||||
|
User findByUserName(String username);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.zhangmeng.oauth.service;
|
||||||
|
|
||||||
|
|
||||||
|
import com.zhangmeng.oauth.dto.AuthToken;
|
||||||
|
|
||||||
|
public interface AuthService {
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 授权认证方法
|
||||||
|
*/
|
||||||
|
AuthToken login(String username, String password, String clientId, String clientSecret);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.zhangmeng.oauth.service;
|
||||||
|
|
||||||
|
|
||||||
|
import com.zhangmeng.model.base.baseService.BaseService;
|
||||||
|
import com.zhangmeng.model.entity.OauthConfig;
|
||||||
|
|
||||||
|
public interface OauthConfigService extends BaseService<OauthConfig> {
|
||||||
|
|
||||||
|
public OauthConfig oauthConfig();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
package com.zhangmeng.oauth.service.impl;
|
||||||
|
|
||||||
|
import com.zhangmeng.oauth.dto.AuthToken;
|
||||||
|
import com.zhangmeng.oauth.service.AuthService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.cloud.client.ServiceInstance;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||||
|
import org.springframework.http.HttpEntity;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.http.client.ClientHttpResponse;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.web.client.DefaultResponseErrorHandler;
|
||||||
|
import org.springframework.web.client.RestClientException;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class AuthServiceImpl implements AuthService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LoadBalancerClient loadBalancerClient;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RestTemplate restTemplate;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 授权认证方法
|
||||||
|
* @param username
|
||||||
|
* @param password
|
||||||
|
* @param clientId
|
||||||
|
* @param clientSecret
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AuthToken login(String username, String password, String clientId, String clientSecret) {
|
||||||
|
//申请令牌
|
||||||
|
AuthToken authToken = applyToken(username,password,clientId, clientSecret);
|
||||||
|
if(authToken == null){
|
||||||
|
throw new RuntimeException("申请令牌失败");
|
||||||
|
}
|
||||||
|
return authToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/****
|
||||||
|
* 认证方法
|
||||||
|
* @param username:用户登录名字
|
||||||
|
* @param password:用户密码
|
||||||
|
* @param clientId:配置文件中的客户端ID
|
||||||
|
* @param clientSecret:配置文件中的秘钥
|
||||||
|
* @return AuthToken
|
||||||
|
*/
|
||||||
|
private AuthToken applyToken(String username, String password, String clientId, String clientSecret) {
|
||||||
|
//选中认证服务的地址
|
||||||
|
ServiceInstance serviceInstance = loadBalancerClient.choose("mystyle-user-oauth");
|
||||||
|
if (serviceInstance == null) {
|
||||||
|
throw new RuntimeException("找不到对应的服务");
|
||||||
|
}
|
||||||
|
//获取令牌的url
|
||||||
|
String path = serviceInstance.getUri().toString() + "/oauth/token";
|
||||||
|
//定义body
|
||||||
|
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
|
||||||
|
//授权方式
|
||||||
|
formData.add("grant_type", "password");
|
||||||
|
//账号
|
||||||
|
formData.add("username", username);
|
||||||
|
//密码
|
||||||
|
formData.add("password", password);
|
||||||
|
//定义头
|
||||||
|
MultiValueMap<String, String> header = new LinkedMultiValueMap<>();
|
||||||
|
header.add("Authorization", httpbasic(clientId, clientSecret));
|
||||||
|
//指定 restTemplate当遇到400或401响应时候也不要抛出异常,也要正常返回值
|
||||||
|
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleError(ClientHttpResponse response) throws IOException {
|
||||||
|
//当响应的值为400或401时候也要正常响应,不要抛出异常
|
||||||
|
if (response.getRawStatusCode() != 400 && response.getRawStatusCode() != 401) {
|
||||||
|
super.handleError(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Map map = null;
|
||||||
|
try {
|
||||||
|
//http请求spring security的申请令牌接口
|
||||||
|
ResponseEntity<Map> mapResponseEntity = restTemplate.exchange(path, HttpMethod.POST,new HttpEntity<MultiValueMap<String, String>>(formData, header), Map.class);
|
||||||
|
//获取响应数据
|
||||||
|
map = mapResponseEntity.getBody();
|
||||||
|
} catch (RestClientException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
if(map == null || map.get("access_token") == null || map.get("refresh_token") == null || map.get("jti") == null) {
|
||||||
|
//jti是jwt令牌的唯一标识作为用户身份令牌
|
||||||
|
throw new RuntimeException("创建令牌失败!");
|
||||||
|
}
|
||||||
|
|
||||||
|
//将响应数据封装成AuthToken对象
|
||||||
|
AuthToken authToken = new AuthToken();
|
||||||
|
//访问令牌(jwt)
|
||||||
|
String accessToken = (String) map.get("access_token");
|
||||||
|
//刷新令牌(jwt)
|
||||||
|
String refreshToken = (String) map.get("refresh_token");
|
||||||
|
//jti,作为用户的身份标识
|
||||||
|
String jwtToken= (String) map.get("jti");
|
||||||
|
authToken.setJti(jwtToken);
|
||||||
|
authToken.setAccessToken(accessToken);
|
||||||
|
authToken.setRefreshToken(refreshToken);
|
||||||
|
return authToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* base64编码
|
||||||
|
* @param clientId
|
||||||
|
* @param clientSecret
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String httpbasic(String clientId,String clientSecret){
|
||||||
|
//将客户端id和客户端密码拼接,按“客户端id:客户端密码”
|
||||||
|
String string = clientId+":"+clientSecret;
|
||||||
|
//进行base64编码
|
||||||
|
byte[] encode = Base64Utils.encode(string.getBytes());
|
||||||
|
return "Basic "+new String(encode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.zhangmeng.oauth.service.impl;
|
||||||
|
|
||||||
|
import com.zhangmeng.model.base.baseService.impl.AbstractBaseServiceImpl;
|
||||||
|
import com.zhangmeng.model.entity.OauthConfig;
|
||||||
|
import com.zhangmeng.oauth.service.OauthConfigService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import tk.mybatis.mapper.entity.Condition;
|
||||||
|
import tk.mybatis.mapper.entity.Example;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class OauthConfigServiceImpl extends AbstractBaseServiceImpl<OauthConfig> implements OauthConfigService {
|
||||||
|
|
||||||
|
public OauthConfig oauthConfig(){
|
||||||
|
Condition condition = new Condition(OauthConfig.class);
|
||||||
|
Example.Criteria criteria = condition.createCriteria();
|
||||||
|
condition.setOrderByClause("addTime desc");
|
||||||
|
List<OauthConfig> oauthConfigs = this.findByCondition(condition);
|
||||||
|
if (oauthConfigs.size() > 0 ){
|
||||||
|
return oauthConfigs.get(0);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.zhangmeng.oauth.utils;
|
||||||
|
|
||||||
|
import javax.servlet.http.Cookie;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class CookieUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置cookie
|
||||||
|
*
|
||||||
|
* @param response
|
||||||
|
* @param name cookie名字
|
||||||
|
* @param value cookie值
|
||||||
|
* @param maxAge cookie生命周期 以秒为单位
|
||||||
|
*/
|
||||||
|
public static void addCookie(HttpServletResponse response, String domain, String path, String name,
|
||||||
|
String value, int maxAge, boolean httpOnly) {
|
||||||
|
Cookie cookie = new Cookie(name, value);
|
||||||
|
cookie.setDomain(domain);
|
||||||
|
cookie.setPath(path);
|
||||||
|
cookie.setMaxAge(maxAge);
|
||||||
|
cookie.setHttpOnly(httpOnly);
|
||||||
|
response.addCookie(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据cookie名称读取cookie
|
||||||
|
* @param request
|
||||||
|
* @return map<cookieName,cookieValue>
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static Map<String,String> readCookie(HttpServletRequest request, String ... cookieNames) {
|
||||||
|
Map<String,String> cookieMap = new HashMap<String,String>();
|
||||||
|
Cookie[] cookies = request.getCookies();
|
||||||
|
if (cookies != null) {
|
||||||
|
for (Cookie cookie : cookies) {
|
||||||
|
String cookieName = cookie.getName();
|
||||||
|
String cookieValue = cookie.getValue();
|
||||||
|
for(int i=0;i<cookieNames.length;i++){
|
||||||
|
if(cookieNames[i].equals(cookieName)){
|
||||||
|
cookieMap.put(cookieName,cookieValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cookieMap;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
package com.zhangmeng.oauth.utils;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.jwt.Jwt;
|
||||||
|
import org.springframework.security.jwt.JwtHelper;
|
||||||
|
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
|
||||||
|
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class TokenTools {
|
||||||
|
|
||||||
|
//公钥
|
||||||
|
private static final String PUBLIC_KEY = "public.key";
|
||||||
|
|
||||||
|
private static String publickey="";
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 获取用户信息
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Map<String,String> getUserInfo(){
|
||||||
|
//获取授权信息
|
||||||
|
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails();
|
||||||
|
//令牌解码
|
||||||
|
return dcodeToken(details.getTokenValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 读取令牌数据
|
||||||
|
*/
|
||||||
|
public Map<String,String> dcodeToken(String token){
|
||||||
|
//校验Jwt
|
||||||
|
Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(getPubKey()));
|
||||||
|
//获取Jwt原始内容
|
||||||
|
String claims = jwt.getClaims();
|
||||||
|
return JSON.parseObject(claims,Map.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取非对称加密公钥 Key
|
||||||
|
* @return 公钥 Key
|
||||||
|
*/
|
||||||
|
public String getPubKey() {
|
||||||
|
if(!StringUtils.isEmpty(publickey)){
|
||||||
|
return publickey;
|
||||||
|
}
|
||||||
|
Resource resource = new ClassPathResource(PUBLIC_KEY);
|
||||||
|
try {
|
||||||
|
InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream());
|
||||||
|
BufferedReader br = new BufferedReader(inputStreamReader);
|
||||||
|
publickey = br.lines().collect(Collectors.joining("\n"));
|
||||||
|
return publickey;
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
server:
|
||||||
|
port: 31006
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: mystyle-cloud-file
|
||||||
|
datasource:
|
||||||
|
username: root
|
||||||
|
password: root
|
||||||
|
url: jdbc:mysql://127.0.0.1:3306/mystyle-blog?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
jpa:
|
||||||
|
database: mysql
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: update
|
||||||
|
naming:
|
||||||
|
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
|
||||||
|
show-sql: true
|
||||||
|
properties:
|
||||||
|
hibernate:
|
||||||
|
dialect: org.hibernate.dialect.MySQL5Dialect
|
||||||
|
zipkin:
|
||||||
|
sender:
|
||||||
|
type: web
|
||||||
|
base-url: http://localhost:9411/
|
||||||
|
service:
|
||||||
|
name: mystyle-cloud-file
|
||||||
|
sleuth:
|
||||||
|
sampler:
|
||||||
|
probability: 1
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
discovery:
|
||||||
|
server-addr: 127.0.0.1:8848
|
||||||
|
mybatis:
|
||||||
|
type-aliases-package: com.zhangmeng.model.entity
|
||||||
|
configuration:
|
||||||
|
mapUnderscoreToCamelCase: true
|
||||||
|
default-enum-type-handler: org.apache.ibatis.type.EnumOrdinalTypeHandler
|
||||||
|
mapper:
|
||||||
|
style: normal
|
||||||
|
enum-as-simple-type: true
|
||||||
|
identity: MYSQL
|
||||||
|
check-example-entity-class: true
|
||||||
Binary file not shown.
|
|
@ -0,0 +1 @@
|
||||||
|
-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAizuj0fBV2+dj4lM3G6efKYvC2czd07BqmzV++E2yBguVks3XWvsW8qlzmG+t1XBCnRFDI/t1Ddc/Jsnlfy4YzRN8otb/Xn6Yz9ACFvZIPGx/q0cqcrgVaR9rSQiSzsGTgUGHNJk8r3A4w9PSSB552Z9s6p5TsWK5ezlfgg+2ANKn1eJ6R/hzajS/B1bTAqYcl9ddo7prneoeAN5LjlMhc2e0cSVgQt8ALP+4x/bTMnDkMjG6R8lnDAxE27B2ZPaLOIOjkUMK+9mZa4RNBoCDG6J/fwPD1NUoVRCbyr/TVaS4EzyhfNK1QW3BlZ0NLSI/SFD3eryKaFQdacJHS31neQIDAQAB-----END PUBLIC KEY-----
|
||||||
8
pom.xml
8
pom.xml
|
|
@ -29,6 +29,7 @@
|
||||||
<module>mystyle-cloud-api</module>
|
<module>mystyle-cloud-api</module>
|
||||||
<module>mystyle-cloud-canal</module>
|
<module>mystyle-cloud-canal</module>
|
||||||
<module>mystyle-cloud-mq</module>
|
<module>mystyle-cloud-mq</module>
|
||||||
|
<module>mystyle-cloud-oauth</module>
|
||||||
|
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
|
|
@ -53,6 +54,7 @@
|
||||||
<opencv-platform.version>4.1.2-1.5.2</opencv-platform.version>
|
<opencv-platform.version>4.1.2-1.5.2</opencv-platform.version>
|
||||||
<ffmpeg-platform.version>4.2.1-1.5.2</ffmpeg-platform.version>
|
<ffmpeg-platform.version>4.2.1-1.5.2</ffmpeg-platform.version>
|
||||||
<fastjson.version>1.2.75</fastjson.version>
|
<fastjson.version>1.2.75</fastjson.version>
|
||||||
|
<jwt.version>0.9.1</jwt.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
|
@ -198,7 +200,11 @@
|
||||||
<artifactId>fastjson</artifactId>
|
<artifactId>fastjson</artifactId>
|
||||||
<version>${fastjson.version}</version>
|
<version>${fastjson.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt</artifactId>
|
||||||
|
<version>${jwt.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
</project>
|
</project>
|
||||||
Loading…
Reference in New Issue