update 2021年11月3日18:23:35

master
zhangmeng 2021-11-03 18:23:50 +08:00
parent 5830c7873b
commit 3faa725ec1
26 changed files with 1148 additions and 1 deletions

View File

@ -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);
}

View File

@ -89,5 +89,15 @@
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -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 20211717: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;
}

View File

@ -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>

View File

@ -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 202111317:00:00
* @version 1.0
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OauthApplication {
public static void main(String[] args) {
SpringApplication.run(OauthApplication.class,args);
}
}

View File

@ -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 202191009: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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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 202191411: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);
}
}

View File

@ -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(); //其他请求都需要经过验证
}
}

View File

@ -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);
}
}

View File

@ -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> {
}

View File

@ -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;
}

View File

@ -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 202191812: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();
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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 clientIdID
* @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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
/**
* cookiecookie
* @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;
}
}

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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-----

View File

@ -29,6 +29,7 @@
<module>mystyle-cloud-api</module>
<module>mystyle-cloud-canal</module>
<module>mystyle-cloud-mq</module>
<module>mystyle-cloud-oauth</module>
</modules>
@ -53,6 +54,7 @@
<opencv-platform.version>4.1.2-1.5.2</opencv-platform.version>
<ffmpeg-platform.version>4.2.1-1.5.2</ffmpeg-platform.version>
<fastjson.version>1.2.75</fastjson.version>
<jwt.version>0.9.1</jwt.version>
</properties>
<dependencyManagement>
@ -198,7 +200,11 @@
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jwt.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>