mirror of
https://github.com/abcv7/sfbx-cloud.git
synced 2026-08-16 11:47:00 +00:00
功能:在保险、规则、交易、积分和短信模块中实现初始功能和基础组件。
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
FROM openjdk:11-jdk
|
||||
LABEL maintainer="研究院研发组 <research@itcast.cn>"
|
||||
|
||||
# 时区修改为东八区
|
||||
ENV TZ=Asia/Shanghai
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
WORKDIR /security-oauth
|
||||
ARG PACKAGE_PATH=./target/security-oauth.jar
|
||||
ADD ${PACKAGE_PATH:-./} security-oauth.jar
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENV JAVA_OPTS="\
|
||||
-server \
|
||||
-Xms256m \
|
||||
-Xmx512m \
|
||||
-XX:MetaspaceSize=256m \
|
||||
-XX:MaxMetaspaceSize=512m\
|
||||
-Dspring.profiles.active=test"
|
||||
ENTRYPOINT ["sh","-c","java -Djava.security.egd=file:/dev/./urandom -jar $JAVA_OPTS security-oauth.jar"]
|
||||
@@ -0,0 +1,88 @@
|
||||
<?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>
|
||||
<parent>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>sfbx-security</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<!--权限处理:认证模块-->
|
||||
<artifactId>security-oauth</artifactId>
|
||||
<name>security-oauth</name>
|
||||
<!-- FIXME change it to the project's website -->
|
||||
<url>http://www.example.com</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-mybatis-plus</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security.oauth.boot</groupId>
|
||||
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-oauth2-jose</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>security-interface</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itheima.sfbx</groupId>
|
||||
<artifactId>framework-web</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/*.jks</include>
|
||||
<include>**/*.yml</include>
|
||||
<include>**/*.properties</include>
|
||||
<include>**/*.xml</include>
|
||||
<include>**/*.yaml</include>
|
||||
<include>**/*.txt</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<finalName>security-oauth</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.itheima.sfbx.security;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
/**
|
||||
* 前后端都需要使用到的登录认证中心
|
||||
*/
|
||||
@SpringBootApplication(scanBasePackages = "com.itheima.sfbx")
|
||||
@EnableDiscoveryClient
|
||||
public class SecurityOauthStart {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SecurityOauthStart.class);
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.itheima.sfbx.security.adepter;
|
||||
|
||||
import com.itheima.sfbx.framework.commons.dto.security.UserVO;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @ClassName LoginAuthAdepter.java
|
||||
* @Description 登录适配接口
|
||||
*/
|
||||
public interface LoginAuthAdepter {
|
||||
|
||||
/***
|
||||
* @description 适配路由
|
||||
* @param principal 认证主体
|
||||
* @param parameters 登录参数
|
||||
* @return
|
||||
*/
|
||||
UserVO adepterRoutes(Principal principal, Map<String, String> parameters) throws HttpRequestMethodNotSupportedException;
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package com.itheima.sfbx.security.adepter.impl;
|
||||
|
||||
import com.itheima.sfbx.framework.commons.constant.security.CompanyCacheConstant;
|
||||
import com.itheima.sfbx.framework.commons.constant.security.OauthConstant;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.CompanyVO;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.UserVO;
|
||||
import com.itheima.sfbx.framework.commons.enums.security.AuthEnum;
|
||||
import com.itheima.sfbx.framework.commons.exception.ProjectException;
|
||||
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
|
||||
import com.itheima.sfbx.framework.commons.utils.RegisterBeanHandler;
|
||||
import com.itheima.sfbx.security.handler.LoginAuthHandler;
|
||||
import com.itheima.sfbx.security.adepter.LoginAuthAdepter;
|
||||
import org.redisson.api.RBucket;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.redisson.codec.JsonJacksonCodec;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.security.Principal;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @ClassName LoginAuthAdepterImpl.java
|
||||
* @Description 登录适配器
|
||||
*/
|
||||
@Component
|
||||
public class LoginAuthAdepterImpl implements LoginAuthAdepter {
|
||||
|
||||
@Autowired
|
||||
RegisterBeanHandler registerBeanHandler;
|
||||
|
||||
@Autowired
|
||||
RedissonClient redissonClient;
|
||||
|
||||
@Autowired
|
||||
HttpServletRequest httpServletRequest;
|
||||
|
||||
@Override
|
||||
public UserVO adepterRoutes(Principal principal, Map<String, String> parameters)
|
||||
throws HttpRequestMethodNotSupportedException {
|
||||
//获得域名
|
||||
String host = httpServletRequest.getHeaders("x-forwarded-host").nextElement().split(",")[0].split(":")[0];
|
||||
String key = CompanyCacheConstant.WEBSITE+host;
|
||||
//域名校验
|
||||
RBucket<CompanyVO> bucket = redissonClient.getBucket(key);
|
||||
CompanyVO companyVO = bucket.get();
|
||||
if (EmptyUtil.isNullOrEmpty(companyVO)){
|
||||
throw new ProjectException(AuthEnum.HSOT_FAIL);
|
||||
}
|
||||
//适配登录方式
|
||||
String loginType = parameters.get(OauthConstant.LOGIN_TYPE_KEY);
|
||||
if (EmptyUtil.isNullOrEmpty(loginType)){
|
||||
throw new ProjectException(AuthEnum.LOGIN_FAIL);
|
||||
}
|
||||
String loginBeanName = OauthConstant.loginBeanNames.get(loginType.split("-")[1]);
|
||||
LoginAuthHandler loginAuthHandler = registerBeanHandler.getBean(loginBeanName,LoginAuthHandler.class);
|
||||
return loginAuthHandler.loginHandler(principal,parameters,loginBeanName,companyVO);
|
||||
}
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
package com.itheima.sfbx.security.base;
|
||||
|
||||
import com.itheima.sfbx.framework.commons.dto.security.DataSecurityVO;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.DeptPostUserVO;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.UserVO;
|
||||
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @ClassName AuthUser.java
|
||||
* @Description 自定认证用户
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class UserAuth implements UserDetails {
|
||||
|
||||
private String id;
|
||||
|
||||
//"用户账号
|
||||
private String username;
|
||||
|
||||
//"密码
|
||||
private String password;
|
||||
|
||||
//权限内置
|
||||
private Collection<SimpleGrantedAuthority> authorities;
|
||||
|
||||
//"用户昵称
|
||||
private String nickName;
|
||||
|
||||
//"用户邮箱
|
||||
private String email;
|
||||
|
||||
//"真实姓名
|
||||
private String realName;
|
||||
|
||||
//"手机号码
|
||||
private String mobile;
|
||||
|
||||
//"用户性别(0男 1女 2未知)
|
||||
private String sex;
|
||||
|
||||
//"创建者
|
||||
private Long createBy;
|
||||
|
||||
//"创建时间
|
||||
private LocalDateTime createTime;
|
||||
|
||||
//"更新者
|
||||
private Long updateBy;
|
||||
|
||||
//"更新时间
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
//"备注
|
||||
private String remark;
|
||||
|
||||
//"三方openId
|
||||
private String openId;
|
||||
|
||||
//"查询用户:所属单位职位
|
||||
private Set<DeptPostUserVO> deptPostUserVOset;
|
||||
|
||||
//"构建令牌:用户角色标识
|
||||
private Set<String> roleLabels;
|
||||
|
||||
//"构建令牌:用户权限路径
|
||||
private Set<String> resourceRequestPaths;
|
||||
|
||||
//"部门编号【当前】
|
||||
private String deptNo;
|
||||
|
||||
//"职位编号【当前】
|
||||
private String postNo;
|
||||
|
||||
private String clientId;
|
||||
|
||||
private DataSecurityVO dataSecurityVO;
|
||||
|
||||
private Boolean onlyAuthenticate ;
|
||||
|
||||
private String companyNo;
|
||||
|
||||
public UserAuth(UserVO userVO) {
|
||||
this.setId(userVO.getId().toString());
|
||||
this.setUsername(userVO.getUsername());
|
||||
this.setPassword(userVO.getPassword());
|
||||
if (!EmptyUtil.isNullOrEmpty(userVO.getResourceRequestPaths())) {
|
||||
authorities = new ArrayList<>();
|
||||
userVO.getResourceRequestPaths().forEach(resourceRequestPath -> authorities.add(new SimpleGrantedAuthority(resourceRequestPath)));
|
||||
}
|
||||
this.setNickName(userVO.getNickName());
|
||||
this.setEmail(userVO.getEmail());
|
||||
this.setRealName(userVO.getRealName());
|
||||
this.setMobile(userVO.getMobile());
|
||||
this.setSex(userVO.getSex());
|
||||
this.setCreateTime(userVO.getCreateTime());
|
||||
this.setCreateBy(userVO.getCreateBy());
|
||||
this.setUpdateTime(userVO.getUpdateTime());
|
||||
this.setUpdateBy(userVO.getUpdateBy());
|
||||
this.setRemark(userVO.getRemark());
|
||||
this.setOpenId(userVO.getOpenId());
|
||||
this.setDeptPostUserVOset(userVO.getDeptPostUserVOs());
|
||||
this.setRoleLabels(userVO.getRoleLabels());
|
||||
this.setResourceRequestPaths(userVO.getResourceRequestPaths());
|
||||
this.setDeptNo(userVO.getDeptNo());
|
||||
this.setPostNo(userVO.getPostNo());
|
||||
this.setClientId(userVO.getClientId());
|
||||
this.setDataSecurityVO(userVO.getDataSecurityVO());
|
||||
this.setOnlyAuthenticate(userVO.getOnlyAuthenticate());
|
||||
this.setCompanyNo(userVO.getCompanyNo());
|
||||
this.setRealName(userVO.getRealName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return this.authorities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
package com.itheima.sfbx.security.config;
|
||||
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.itheima.sfbx.framework.commons.basic.ResponseResult;
|
||||
import com.itheima.sfbx.framework.commons.constant.security.OauthConstant;
|
||||
import com.itheima.sfbx.framework.commons.enums.security.AuthEnum;
|
||||
import com.itheima.sfbx.framework.commons.properties.SecurityConfigProperties;
|
||||
import com.itheima.sfbx.framework.commons.utils.ResponseResultBuild;
|
||||
import com.itheima.sfbx.security.base.UserAuth;
|
||||
import com.itheima.sfbx.security.details.JdbcClientDetailsServiceImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
|
||||
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.client.JdbcClientDetailsService;
|
||||
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
|
||||
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 org.springframework.security.web.AuthenticationEntryPoint;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.security.KeyPair;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 授权服务配置
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAuthorizationServer
|
||||
@EnableConfigurationProperties(SecurityConfigProperties.class)
|
||||
public class Oauth2ServerConfig extends AuthorizationServerConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
@Autowired
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
@Autowired
|
||||
private UserDetailsService oauth2UserDetailsService;
|
||||
|
||||
@Autowired
|
||||
SecurityConfigProperties securityConfigProperties;
|
||||
|
||||
/***
|
||||
* @description 客户端信息到数据库中
|
||||
* @param clients 客户端配置形象
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
|
||||
clients.withClientDetails(jdbcClientDetailsService());
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 客户端信息JDBC管理
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public JdbcClientDetailsService jdbcClientDetailsService(){
|
||||
JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsServiceImpl(dataSource);
|
||||
jdbcClientDetailsService.setFindClientDetailsSql(OauthConstant.FIND_CLIENT_DETAILS_SQL);
|
||||
jdbcClientDetailsService.setSelectClientDetailsSql(OauthConstant.SELECT_CLIENT_DETAILS_SQL);
|
||||
return jdbcClientDetailsService;
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 授权服务器端点的非安全特性如令牌存储、令牌自定义、用户批准和授权类型
|
||||
* @param endpoints 认证服务令牌站点
|
||||
*/
|
||||
@Override
|
||||
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
|
||||
endpoints
|
||||
.authenticationManager(authenticationManager)//配置认证管理器
|
||||
.userDetailsService(oauth2UserDetailsService)//配置oauth2User查询明细
|
||||
.accessTokenConverter(jwtAccessTokenConverter())//配置令牌转换器
|
||||
.tokenServices(jwtTokenService())//配置令牌服务(token services)
|
||||
.reuseRefreshTokens(true);//是否沿用刷新令牌直到过期,这里每次刷新直接后使用新的刷新令牌下次刷新
|
||||
}
|
||||
|
||||
/**
|
||||
* 令牌服务:持久化令牌相关服务
|
||||
*/
|
||||
@Bean
|
||||
public DefaultTokenServices jwtTokenService() {
|
||||
DefaultTokenServices service=new DefaultTokenServices();
|
||||
//存储策略
|
||||
JwtTokenStore jwtTokenStore = new JwtTokenStore(jwtAccessTokenConverter());
|
||||
service.setTokenStore(jwtTokenStore);//令牌存储的持久性策略。
|
||||
service.setClientDetailsService(jdbcClientDetailsService());//指定客戶端明细信息
|
||||
service.setSupportRefreshToken(true);//是否支持刷新令牌
|
||||
service.setTokenEnhancer(tokenEnhancerChain());//在将新令牌保存到令牌存储区之前,将应用于新令牌的访问令牌增强器。
|
||||
service.setAccessTokenValiditySeconds(securityConfigProperties.getAccessTokenValiditySeconds()); // 令牌默认有效期2小时
|
||||
service.setRefreshTokenValiditySeconds(securityConfigProperties.getRefreshTokenValiditySeconds()); // 刷新令牌默认有效期3天
|
||||
return service;
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* @description 复合令牌增强器:把构建的JWT令牌及增强属性填充到OAuth2AccessToken中
|
||||
*/
|
||||
@Bean
|
||||
public TokenEnhancerChain tokenEnhancerChain(){
|
||||
//token令牌增强者List
|
||||
List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
|
||||
//扩展JWT内容增强,可携带更多对象信息:企业号、门店ID等
|
||||
tokenEnhancers.add(tokenEnhancer());
|
||||
//使用非对称加密算法对token签名
|
||||
tokenEnhancers.add(jwtAccessTokenConverter());
|
||||
//构建增强链:填入token令牌增强者List
|
||||
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
|
||||
tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);
|
||||
return tokenEnhancerChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用非对称加密算法对token签名
|
||||
*/
|
||||
@Bean
|
||||
public JwtAccessTokenConverter jwtAccessTokenConverter() {
|
||||
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
|
||||
//从classpath下的密钥库中获取密钥对(公钥+私钥)
|
||||
converter.setKeyPair(keyPair());
|
||||
return converter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从classpath下的密钥库中获取密钥对(公钥+私钥)
|
||||
*/
|
||||
@Bean
|
||||
public KeyPair keyPair() {
|
||||
KeyStoreKeyFactory factory = new KeyStoreKeyFactory(
|
||||
new ClassPathResource("itheima.jks"), "shuwenqi".toCharArray());
|
||||
KeyPair keyPair = factory.getKeyPair(
|
||||
"itheima", "shuwenqi".toCharArray());
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
/**
|
||||
* JWT内容增强
|
||||
*/
|
||||
@Bean
|
||||
public TokenEnhancer tokenEnhancer() {
|
||||
return (accessToken, authentication) -> {
|
||||
Map<String, Object> map = new HashMap<>(10);
|
||||
Object principal = authentication.getUserAuthentication().getPrincipal();
|
||||
if (principal instanceof UserAuth){
|
||||
UserAuth userAuth = (UserAuth)principal ;
|
||||
map.put(OauthConstant.USER_ID_KEY, userAuth.getId());
|
||||
map.put(OauthConstant.REAL_NAME_KEY, userAuth.getRealName());
|
||||
map.put(OauthConstant.CLIENT_ID_KEY, userAuth.getClientId());
|
||||
map.put(OauthConstant.USER_NAME_KEY,userAuth.getUsername());
|
||||
map.put(OauthConstant.SEX_KEY,userAuth.getSex());
|
||||
map.put(OauthConstant.RESOURCS_KEY,userAuth.getResourceRequestPaths());
|
||||
map.put(OauthConstant.ROLES_KEY,userAuth.getRoleLabels());
|
||||
map.put(OauthConstant.OPEN_ID_KEY,userAuth.getOpenId());
|
||||
map.put(OauthConstant.DEPT_NO_KEY,userAuth.getDeptNo());
|
||||
map.put(OauthConstant.POST_NO_KEY,userAuth.getPostNo());
|
||||
map.put(OauthConstant.MOBILE_KEY,userAuth.getMobile());
|
||||
map.put(OauthConstant.ONLY_AUTHENTICATE_KEY,userAuth.getOnlyAuthenticate());
|
||||
map.put(OauthConstant.DATA_SECURITY_KEY,userAuth.getDataSecurityVO());
|
||||
map.put(OauthConstant.COMPANY_NO_KEY,userAuth.getCompanyNo());
|
||||
map.put(OauthConstant.EXPIRES_IN_KEY,securityConfigProperties.getAccessTokenValiditySeconds());
|
||||
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(map);
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
};
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 认证异常处理配置
|
||||
* @return:
|
||||
*/
|
||||
@Override
|
||||
public void configure(AuthorizationServerSecurityConfigurer security) {
|
||||
security.authenticationEntryPoint(authenticationEntryPoint())//异常处理
|
||||
.tokenKeyAccess("permitAll()")
|
||||
.checkTokenAccess("permitAll()")
|
||||
.allowFormAuthenticationForClients();
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* @description 自定义认证异常响应数据
|
||||
* @return: org.springframework.security.web.AuthenticationEntryPoint
|
||||
*/
|
||||
@Bean
|
||||
public AuthenticationEntryPoint authenticationEntryPoint() {
|
||||
return (request, response, e) -> {
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
|
||||
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
ResponseResult<Boolean> responseWrap = ResponseResultBuild.build(AuthEnum.NEED_LOGIN, false);
|
||||
String result = JSONObject.toJSONString(responseWrap);
|
||||
response.getWriter().print(result);
|
||||
response.getWriter().flush();
|
||||
};
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.itheima.sfbx.security.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @ClassName RestTemplateConfig.java
|
||||
* @Description 远程调用
|
||||
*/
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
//请求客户端
|
||||
@Bean
|
||||
public RestTemplate restTemplate(){
|
||||
return new RestTemplate();
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package com.itheima.sfbx.security.config;
|
||||
|
||||
import com.itheima.sfbx.framework.commons.properties.SecurityConfigProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @ClassName ReactiveSecurityConfig.java
|
||||
* @Description 支持web的权限配置
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableConfigurationProperties(SecurityConfigProperties.class)
|
||||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
SecurityConfigProperties securityConfigProperties;
|
||||
|
||||
@Autowired
|
||||
UserDetailsService oauth2UserDetailsService;
|
||||
|
||||
/**
|
||||
* BCrypt密码编码
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public BCryptPasswordEncoder bcryptPasswordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义身份认证逻辑
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
//指定用户明细查询及密码处理者
|
||||
auth.userDetailsService(oauth2UserDetailsService).passwordEncoder(bcryptPasswordEncoder());
|
||||
super.configure(auth);
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证管理器
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
//使用默认的认证管理器
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 过滤器链定义
|
||||
* @param http
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.cors()
|
||||
.and()
|
||||
.authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()//站点请求处理
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.antMatchers(securityConfigProperties.getIgnoreUrl()
|
||||
.toArray(new String[securityConfigProperties.getIgnoreUrl().size()]))//忽略配置
|
||||
.permitAll()
|
||||
.anyRequest().authenticated()//其他请求都需要校验
|
||||
.and()
|
||||
.csrf().disable();
|
||||
}
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.itheima.sfbx.security.details;
|
||||
|
||||
import org.springframework.security.oauth2.provider.ClientDetails;
|
||||
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/***
|
||||
* @description 对JdbcClientDetailsService的增强
|
||||
*/
|
||||
public class JdbcClientDetailsServiceImpl extends JdbcClientDetailsService {
|
||||
|
||||
//使用数据库中配置进行对象构建
|
||||
public JdbcClientDetailsServiceImpl(DataSource dataSource) {
|
||||
super(dataSource);
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 加载指定客户端配置信息
|
||||
* @param clientId
|
||||
* @return
|
||||
* @return: org.springframework.security.oauth2.provider.ClientDetails
|
||||
*/
|
||||
@Override
|
||||
public ClientDetails loadClientByClientId(String clientId) {
|
||||
return super.loadClientByClientId(clientId);
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package com.itheima.sfbx.security.details;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.*;
|
||||
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
|
||||
import com.itheima.sfbx.framework.commons.utils.RegisterBeanHandler;
|
||||
import com.itheima.sfbx.security.feign.CustomerFeign;
|
||||
import com.itheima.sfbx.security.feign.UserFeign;
|
||||
import com.itheima.sfbx.security.handler.LoginAuthHandler;
|
||||
import com.itheima.sfbx.security.base.UserAuth;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.DisabledException;
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @ClassName ReactiveUserDetailsServiceImpl.java
|
||||
* @Description 支持flum的身份类实现ReactiveUserDetailsService接口
|
||||
*/
|
||||
@Slf4j
|
||||
@Component("oauth2UserDetailsService")
|
||||
public class Oauth2UserDetailsServiceImpl implements UserDetailsService {
|
||||
|
||||
//调用RPC原创服务
|
||||
@Autowired
|
||||
UserFeign userFeign;
|
||||
|
||||
//调用RPC原创服务
|
||||
@Autowired
|
||||
CustomerFeign customerFeign;
|
||||
|
||||
@Autowired
|
||||
RegisterBeanHandler registerBeanHandler;
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String principal) throws UsernameNotFoundException {
|
||||
UsernameVO usernameVO = JSONObject.parseObject(principal,UsernameVO.class);
|
||||
//查询用户明细
|
||||
LoginAuthHandler loginAuthHandler = registerBeanHandler
|
||||
.getBean(usernameVO.getLoginBeanName(),LoginAuthHandler.class);
|
||||
UserVO userVO = loginAuthHandler.findUserDetail(usernameVO.getLoginType(),
|
||||
usernameVO.getUsername(),usernameVO.getCompanyNo());
|
||||
//UserAuth构建
|
||||
if (EmptyUtil.isNullOrEmpty(userVO)) {
|
||||
throw new DisabledException("无效的账号");
|
||||
}
|
||||
if (SuperConstant.DATA_STATE_1.equals(userVO.getDataState())) {
|
||||
throw new LockedException("账户被禁用");
|
||||
}
|
||||
//客户端
|
||||
userVO.setClientId(usernameVO.getClientId());
|
||||
//资源
|
||||
List<ResourceVO> resourceVOs = userFeign.findResourceByUserId(userVO.getId());
|
||||
if (!EmptyUtil.isNullOrEmpty(resourceVOs)){
|
||||
Set<String> resources = resourceVOs.stream().map(ResourceVO::getRequestPath).collect(Collectors.toSet());
|
||||
userVO.setResourceRequestPaths(resources);
|
||||
}
|
||||
//角色
|
||||
List<RoleVO> roleVOs = userFeign.findRoleByUserId(userVO.getId());
|
||||
if (!EmptyUtil.isNullOrEmpty(roleVOs)){
|
||||
Set<String> roleLabel = roleVOs.stream().map(RoleVO::getLabel).collect(Collectors.toSet());
|
||||
userVO.setRoleLabels(roleLabel);
|
||||
}
|
||||
//数据权限
|
||||
QueryDataSecurityVO queryDataSecurityVO = QueryDataSecurityVO.builder()
|
||||
.userId(userVO.getId())
|
||||
.roleVOs(roleVOs)
|
||||
.build();
|
||||
DataSecurityVO dataSecurityVO = userFeign.userDataSecurity(queryDataSecurityVO);
|
||||
if (!EmptyUtil.isNullOrEmpty(dataSecurityVO)){
|
||||
userVO.setDataSecurityVO(dataSecurityVO);
|
||||
}
|
||||
|
||||
return new UserAuth(userVO);
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.itheima.sfbx.security.handler;
|
||||
|
||||
import com.itheima.sfbx.framework.commons.dto.security.UserVO;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
|
||||
/**
|
||||
* @ClassName CacheTokenHandler.java
|
||||
* @Description 缓存token处理器
|
||||
*/
|
||||
public interface CacheTokenHandler {
|
||||
|
||||
|
||||
/***
|
||||
* @description 缓存token
|
||||
* @param oAuth2AccessToken
|
||||
* @return
|
||||
*/
|
||||
UserVO cacheToken(OAuth2AccessToken oAuth2AccessToken);
|
||||
|
||||
/***
|
||||
* @description 缓存token
|
||||
* @param userVO 用户
|
||||
* @return
|
||||
*/
|
||||
Boolean deleteToken(UserVO userVO);
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.itheima.sfbx.security.handler;
|
||||
|
||||
import com.itheima.sfbx.framework.commons.dto.security.CompanyVO;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.UserVO;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @ClassName LoginAdapter.java
|
||||
* @Description 登陆处理器接口
|
||||
*/
|
||||
public interface LoginAuthHandler {
|
||||
|
||||
/***
|
||||
* @description 登录处理
|
||||
* @param parameters 登录参数
|
||||
* @return
|
||||
*/
|
||||
UserVO loginHandler(Principal principal, Map<String, String> parameters,
|
||||
String loginBeanName, CompanyVO companyVO)
|
||||
throws HttpRequestMethodNotSupportedException;
|
||||
|
||||
/***
|
||||
* @description 用户信息查询
|
||||
* @param loginType 登录类型
|
||||
* @param username 账号信息:用户名或者手机号或者openId
|
||||
* @param companyNo 企业号
|
||||
* @return: com.itheima.easy.vo.security.UserVO
|
||||
*/
|
||||
UserVO findUserDetail(String loginType,String username,String companyNo);
|
||||
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.itheima.sfbx.security.handler;
|
||||
|
||||
import com.itheima.sfbx.framework.commons.dto.security.UserVO;
|
||||
|
||||
/**
|
||||
* @ClassName LogoutHandler.java
|
||||
* @Description 退出处理器
|
||||
*/
|
||||
public interface LogoutHandler {
|
||||
|
||||
/***
|
||||
* @description 退出
|
||||
* @param userVO 退出用户
|
||||
* @return
|
||||
*/
|
||||
Boolean logout(UserVO userVO);
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.itheima.sfbx.security.handler;
|
||||
|
||||
import com.itheima.sfbx.framework.commons.dto.security.UserVO;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @ClassName RefreshTokenHandler.java
|
||||
* @Description 刷新令牌处理
|
||||
*/
|
||||
public interface RefreshTokenHandler {
|
||||
|
||||
/***
|
||||
* @description 刷新令牌
|
||||
* @return
|
||||
*/
|
||||
public UserVO refreshToken(Principal principal, Map<String, String> parameters);
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package com.itheima.sfbx.security.handler.impl;
|
||||
|
||||
import com.itheima.sfbx.framework.commons.constant.security.OauthCacheConstant;
|
||||
import com.itheima.sfbx.framework.commons.properties.SecurityConfigProperties;
|
||||
import com.itheima.sfbx.framework.commons.constant.security.OauthConstant;
|
||||
import com.itheima.sfbx.security.handler.CacheTokenHandler;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.DataSecurityVO;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.UserVO;
|
||||
import org.redisson.api.RBucket;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @ClassName CacheTokenHandlerImpl.java
|
||||
* @Description Token缓存处理
|
||||
*/
|
||||
@Component
|
||||
@EnableConfigurationProperties(SecurityConfigProperties.class)
|
||||
public class CacheTokenHandlerImpl implements CacheTokenHandler {
|
||||
|
||||
@Autowired
|
||||
RedissonClient redissonClient;
|
||||
|
||||
@Autowired
|
||||
SecurityConfigProperties securityConfigProperties;
|
||||
|
||||
@Override
|
||||
public UserVO cacheToken(OAuth2AccessToken oAuth2AccessToken){
|
||||
//登录成功后获得oAuth2AccessToken中增强信息
|
||||
Long id = Long.valueOf(oAuth2AccessToken.getAdditionalInformation().get(OauthConstant.USER_ID_KEY).toString());
|
||||
String userToken =String.valueOf(oAuth2AccessToken.getAdditionalInformation().get(OauthConstant.JTI_KEY));
|
||||
String mobile =String.valueOf(oAuth2AccessToken.getAdditionalInformation().get(OauthConstant.MOBILE_KEY));
|
||||
String openId =String.valueOf(oAuth2AccessToken.getAdditionalInformation().get(OauthConstant.OPEN_ID_KEY));
|
||||
Set<String> resources = (Set<String>) oAuth2AccessToken.getAdditionalInformation().get(OauthConstant.RESOURCS_KEY);
|
||||
Set<String> roles = (Set<String>) oAuth2AccessToken.getAdditionalInformation().get(OauthConstant.ROLES_KEY);
|
||||
String username = String.valueOf(oAuth2AccessToken.getAdditionalInformation().get(OauthConstant.USER_NAME_KEY));
|
||||
String sex = String.valueOf(oAuth2AccessToken.getAdditionalInformation().get(OauthConstant.SEX_KEY));
|
||||
String clientId = String.valueOf(oAuth2AccessToken.getAdditionalInformation().get(OauthConstant.CLIENT_ID_KEY));
|
||||
String realName = String.valueOf(oAuth2AccessToken.getAdditionalInformation().get(OauthConstant.REAL_NAME_KEY));
|
||||
String deptNo = String.valueOf(oAuth2AccessToken.getAdditionalInformation().get(OauthConstant.DEPT_NO_KEY));
|
||||
String postNo = String.valueOf(oAuth2AccessToken.getAdditionalInformation().get(OauthConstant.POST_NO_KEY));
|
||||
Boolean onlyAuthenticate = (Boolean) oAuth2AccessToken.getAdditionalInformation().get(OauthConstant.ONLY_AUTHENTICATE_KEY);
|
||||
DataSecurityVO dataSecurityVO = (DataSecurityVO) oAuth2AccessToken.getAdditionalInformation().get(OauthConstant.DATA_SECURITY_KEY);
|
||||
String companyNo = String.valueOf(oAuth2AccessToken.getAdditionalInformation().get(OauthConstant.COMPANY_NO_KEY));
|
||||
//设置accessToken到redis,获得时使用userToken获取
|
||||
String accessToken = oAuth2AccessToken.getValue();
|
||||
RBucket<String> accessTokenBucket = redissonClient.getBucket(OauthCacheConstant.ACCESS_TOKEN + userToken);
|
||||
long accessTokenExpiresIn = Long.valueOf(oAuth2AccessToken.getAdditionalInformation().get(OauthConstant.EXPIRES_IN_KEY).toString());
|
||||
accessTokenBucket.set(accessToken,accessTokenExpiresIn, TimeUnit.SECONDS);
|
||||
//设置refreshToken到redis,获得时使用userToken获取
|
||||
OAuth2RefreshToken oAuth2RefreshToken = oAuth2AccessToken.getRefreshToken();
|
||||
String refreshToken = oAuth2RefreshToken.getValue();
|
||||
long refreshTokenExpiresIn = securityConfigProperties.getRefreshTokenValiditySeconds();
|
||||
RBucket<String> refreshTokenBucket = redissonClient.getBucket(OauthCacheConstant.REFRESH_TOKEN + userToken);
|
||||
refreshTokenBucket.set(refreshToken,refreshTokenExpiresIn, TimeUnit.SECONDS);
|
||||
//绑定username与userToken,用于剔除设置
|
||||
RBucket<String> usernameBucket = redissonClient.getBucket(OauthCacheConstant.USER_TOKEN_BIND + username);
|
||||
usernameBucket.set(userToken,refreshTokenExpiresIn, TimeUnit.SECONDS);
|
||||
//构建返回对象
|
||||
UserVO userVO = UserVO.builder()
|
||||
.id(id)
|
||||
.clientId(clientId)
|
||||
.companyNo(companyNo)
|
||||
.username(username)
|
||||
.userToken(userToken)
|
||||
.resourceRequestPaths(resources)
|
||||
.roleLabels(roles)
|
||||
.openId(openId)
|
||||
.mobile(mobile)
|
||||
.deptNo(deptNo)
|
||||
.postNo(postNo)
|
||||
.dataSecurityVO(dataSecurityVO)
|
||||
.onlyAuthenticate(onlyAuthenticate)
|
||||
.sex(sex)
|
||||
.realName(realName)
|
||||
.build();
|
||||
//设置userVO到redis,获得时候使用userToken获得
|
||||
RBucket<UserVO> userTokenBucket = redissonClient.getBucket(OauthCacheConstant.USER_TOKEN + userToken);
|
||||
userTokenBucket.set(userVO,refreshTokenExpiresIn, TimeUnit.SECONDS);
|
||||
return userVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteToken(UserVO userVO) {
|
||||
//删除accessToken
|
||||
RBucket<String> accessTokenBucket = redissonClient.getBucket(OauthCacheConstant.ACCESS_TOKEN + userVO.getUserToken());
|
||||
boolean deleteAccessToken = accessTokenBucket.delete();
|
||||
//删除refreshToken
|
||||
RBucket<String> refreshTokenBucket = redissonClient.getBucket(OauthCacheConstant.REFRESH_TOKEN + userVO.getUserToken());
|
||||
boolean deleteRefreshToken = refreshTokenBucket.delete();
|
||||
//删除绑定username
|
||||
RBucket<String> usernameBucket = redissonClient.getBucket(OauthCacheConstant.USER_TOKEN_BIND + userVO.getUsername());
|
||||
boolean deleteUsername = usernameBucket.delete();
|
||||
//删除用户和userToken关联
|
||||
RBucket<UserVO> userTokenBucket = redissonClient.getBucket(OauthCacheConstant.USER_TOKEN + userVO.getUserToken());
|
||||
boolean deleteUserTokenBucket = userTokenBucket.delete();
|
||||
//清理完成
|
||||
if (deleteAccessToken&&deleteRefreshToken&&deleteUsername&&deleteUserTokenBucket){
|
||||
return true;
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.itheima.sfbx.security.handler.impl;
|
||||
|
||||
import com.itheima.sfbx.security.handler.CacheTokenHandler;
|
||||
import com.itheima.sfbx.security.handler.LogoutHandler;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.UserVO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @ClassName LogoutHandlerImpl.java
|
||||
* @Description 退出处理实现
|
||||
*/
|
||||
@Component
|
||||
public class LogoutHandlerImpl implements LogoutHandler {
|
||||
|
||||
@Autowired
|
||||
CacheTokenHandler cacheTokenHandler;
|
||||
|
||||
@Override
|
||||
public Boolean logout(UserVO userVO) {
|
||||
|
||||
return cacheTokenHandler.deleteToken(userVO);
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package com.itheima.sfbx.security.handler.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.itheima.sfbx.framework.commons.constant.security.OauthCacheConstant;
|
||||
import com.itheima.sfbx.framework.commons.constant.security.OauthConstant;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.CompanyVO;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.UserVO;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.UsernameVO;
|
||||
import com.itheima.sfbx.framework.commons.enums.security.AuthEnum;
|
||||
import com.itheima.sfbx.framework.commons.exception.ProjectException;
|
||||
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
|
||||
import com.itheima.sfbx.security.handler.CacheTokenHandler;
|
||||
import com.itheima.sfbx.security.handler.LoginAuthHandler;
|
||||
import com.itheima.sfbx.security.feign.CustomerFeign;
|
||||
import com.itheima.sfbx.security.feign.UserFeign;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RBucket;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @ClassName MobileLoginAuthHandlerImpl.java
|
||||
* @Description 手机验证码登录处理器实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Component("mobileLoginAuthHandler")
|
||||
public class MobileLoginAuthHandlerImpl implements LoginAuthHandler {
|
||||
|
||||
@Autowired
|
||||
TokenEndpoint tokenEndpoint;
|
||||
|
||||
@Autowired
|
||||
CacheTokenHandler cacheTokenHandler;
|
||||
|
||||
//调用RPC原创服务
|
||||
@Autowired
|
||||
UserFeign userFeign;
|
||||
|
||||
//调用RPC原创服务
|
||||
@Autowired
|
||||
CustomerFeign customerFeign;
|
||||
|
||||
@Autowired
|
||||
RedissonClient redissonClient;
|
||||
|
||||
@Autowired
|
||||
BCryptPasswordEncoder bCryptPasswordEncoder;
|
||||
|
||||
@Override
|
||||
public UserVO loginHandler(Principal principal, Map<String, String> parameters,
|
||||
String loginBeanName, CompanyVO companyVO)
|
||||
throws HttpRequestMethodNotSupportedException {
|
||||
String mobile = parameters.get(OauthConstant.MOBILE_KEY);
|
||||
String clientId = parameters.get(OauthConstant.CLIENT_ID_KEY);
|
||||
String loginType = parameters.get(OauthConstant.LOGIN_TYPE_KEY);
|
||||
UsernameVO usernameVO = UsernameVO.builder()
|
||||
.username(mobile)
|
||||
.clientId(clientId)
|
||||
.companyNo(companyVO.getCompanyNo())
|
||||
.loginType(loginType)
|
||||
.loginBeanName(loginBeanName)
|
||||
.build();
|
||||
String username = JSONObject.toJSONString(usernameVO);
|
||||
parameters.put(OauthConstant.USER_NAME_KEY,username);
|
||||
OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
|
||||
return cacheTokenHandler.cacheToken(oAuth2AccessToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserVO findUserDetail(String loginType, String mobile,String companyNo) {
|
||||
//手机登录处理
|
||||
String key = OauthCacheConstant.LOGIN_CODE+mobile;
|
||||
//存储手机发送的验证码到存在中
|
||||
// RBucket<String> code = redissonClient.getBucket(key);
|
||||
// if (EmptyUtil.isNullOrEmpty(code.get())){
|
||||
// throw new ProjectException(AuthEnum.CODE_FAIL);
|
||||
// }
|
||||
// String password = bCryptPasswordEncoder.encode(code.get());
|
||||
String password = bCryptPasswordEncoder.encode("123456");
|
||||
//处理登录
|
||||
UserVO userVO = null;
|
||||
switch (loginType){
|
||||
case OauthConstant.USER_MOBILE:
|
||||
userVO = userFeign.mobileLogin(mobile,companyNo);
|
||||
if (!EmptyUtil.isNullOrEmpty(userVO)){
|
||||
userVO.setPassword(password);
|
||||
userVO.setOnlyAuthenticate(false);
|
||||
}
|
||||
return userVO;
|
||||
case OauthConstant.CUSTOMER_MOBILE:
|
||||
userVO = customerFeign.mobileLogin(mobile,companyNo);
|
||||
if (!EmptyUtil.isNullOrEmpty(userVO)){
|
||||
userVO.setPassword(password);
|
||||
userVO.setOnlyAuthenticate(true);
|
||||
}
|
||||
return userVO;
|
||||
default:
|
||||
throw new ProjectException(AuthEnum.LOGIN_FAIL);
|
||||
}
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package com.itheima.sfbx.security.handler.impl;
|
||||
|
||||
import com.itheima.sfbx.framework.commons.constant.security.OauthCacheConstant;
|
||||
import com.itheima.sfbx.framework.commons.constant.security.OauthConstant;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.UserVO;
|
||||
import com.itheima.sfbx.framework.commons.properties.SecurityConfigProperties;
|
||||
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
|
||||
import com.itheima.sfbx.framework.commons.utils.ExceptionsUtil;
|
||||
import com.itheima.sfbx.security.handler.CacheTokenHandler;
|
||||
import com.itheima.sfbx.security.handler.RefreshTokenHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RBucket;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @ClassName RefreshTokenHandlerImpl.java
|
||||
* @Description 刷新令牌处理实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RefreshTokenHandlerImpl implements RefreshTokenHandler {
|
||||
|
||||
@Autowired
|
||||
RedissonClient redissonClient;
|
||||
|
||||
@Autowired
|
||||
TokenEndpoint tokenEndpoint;
|
||||
|
||||
@Autowired
|
||||
CacheTokenHandler cacheTokenHandler;
|
||||
|
||||
@Autowired
|
||||
UserDetailsService userDetailsService;
|
||||
|
||||
@Autowired
|
||||
SecurityConfigProperties securityConfigProperties;
|
||||
|
||||
@Override
|
||||
public UserVO refreshToken(Principal principal, Map<String, String> parameters) {
|
||||
Boolean flag = false;
|
||||
//通过jti获得refreshToken
|
||||
String userToken = parameters.get(OauthConstant.JTI_KEY);
|
||||
RBucket<String> refreshTokenBucket = redissonClient.getBucket(OauthCacheConstant.REFRESH_TOKEN + userToken);
|
||||
String refreshToken = refreshTokenBucket.get();
|
||||
if (EmptyUtil.isNullOrEmpty(refreshToken)){
|
||||
return null;
|
||||
}
|
||||
//使用refreshToken再次获得OAuth2AccessToken
|
||||
parameters.put("refresh_token",refreshToken);
|
||||
OAuth2AccessToken oAuth2AccessToken = null;
|
||||
try {
|
||||
oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
|
||||
} catch (HttpRequestMethodNotSupportedException e) {
|
||||
log.error("刷新令牌出错:{}", ExceptionsUtil.getErrorMessageWithNestedException(e));
|
||||
return null;
|
||||
}
|
||||
//重新构建jwt增强信息
|
||||
DefaultOAuth2AccessToken defaultOAuth2AccessToken = (DefaultOAuth2AccessToken) oAuth2AccessToken;
|
||||
RBucket<UserVO> jtiUserBucket = redissonClient.getBucket(OauthCacheConstant.USER_TOKEN + userToken);
|
||||
UserVO userVO = jtiUserBucket.get();
|
||||
Map<String, Object> map = new HashMap<>(11);
|
||||
map.put(OauthConstant.USER_ID_KEY, userVO.getId());
|
||||
map.put(OauthConstant.CLIENT_ID_KEY, userVO.getClientId());
|
||||
map.put(OauthConstant.USER_NAME_KEY,userVO.getUsername());
|
||||
map.put(OauthConstant.RESOURCS_KEY,userVO.getResourceRequestPaths());
|
||||
map.put(OauthConstant.ROLES_KEY,userVO.getRoleLabels());
|
||||
map.put(OauthConstant.OPEN_ID_KEY,userVO.getOpenId());
|
||||
map.put(OauthConstant.DEPT_NO_KEY,userVO.getDeptNo());
|
||||
map.put(OauthConstant.POST_NO_KEY,userVO.getPostNo());
|
||||
map.put(OauthConstant.MOBILE_KEY,userVO.getMobile());
|
||||
map.put(OauthConstant.COMPANY_NO_KEY,userVO.getCompanyNo());
|
||||
map.put(OauthConstant.DATA_SECURITY_KEY,userVO.getDataSecurityVO());
|
||||
//令牌自定过期时间
|
||||
map.put(OauthConstant.EXPIRES_IN_KEY,securityConfigProperties.getAccessTokenValiditySeconds());
|
||||
defaultOAuth2AccessToken.setAdditionalInformation(map);
|
||||
//处理缓存信息
|
||||
return cacheTokenHandler.cacheToken(defaultOAuth2AccessToken);
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package com.itheima.sfbx.security.handler.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.itheima.sfbx.framework.commons.constant.security.OauthConstant;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.CompanyVO;
|
||||
import com.itheima.sfbx.framework.commons.enums.security.AuthEnum;
|
||||
import com.itheima.sfbx.framework.commons.exception.ProjectException;
|
||||
import com.itheima.sfbx.security.handler.CacheTokenHandler;
|
||||
import com.itheima.sfbx.security.handler.LoginAuthHandler;
|
||||
import com.itheima.sfbx.security.feign.CustomerFeign;
|
||||
import com.itheima.sfbx.security.feign.UserFeign;
|
||||
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.UserVO;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.UsernameVO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @ClassName UserpasswordLoginAdapter.java
|
||||
* @Description 密码登录处理器实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Component("usernameLoginAuthHandler")
|
||||
public class UsernameLoginAuthHandlerImpl implements LoginAuthHandler {
|
||||
|
||||
@Autowired
|
||||
TokenEndpoint tokenEndpoint;
|
||||
|
||||
@Autowired
|
||||
CacheTokenHandler cacheTokenHandler;
|
||||
|
||||
//调用RPC原创服务
|
||||
@Autowired
|
||||
UserFeign userFeign;
|
||||
|
||||
//调用RPC原创服务
|
||||
@Autowired
|
||||
CustomerFeign customerFeign;
|
||||
|
||||
@Override
|
||||
public UserVO loginHandler(Principal principal, Map<String, String> parameters,
|
||||
String loginBeanName, CompanyVO companyVO)
|
||||
throws HttpRequestMethodNotSupportedException {
|
||||
String username = parameters.get(OauthConstant.USER_NAME_KEY);
|
||||
String clientId = parameters.get(OauthConstant.CLIENT_ID_KEY);
|
||||
String loginType = parameters.get(OauthConstant.LOGIN_TYPE_KEY);
|
||||
UsernameVO usernameVO = UsernameVO.builder()
|
||||
.username(username)
|
||||
.companyNo(companyVO.getCompanyNo())
|
||||
.clientId(clientId)
|
||||
.loginType(loginType)
|
||||
.loginBeanName(loginBeanName)
|
||||
.build();
|
||||
username = JSONObject.toJSONString(usernameVO);
|
||||
parameters.put(OauthConstant.USER_NAME_KEY,username);
|
||||
OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
|
||||
return cacheTokenHandler.cacheToken(oAuth2AccessToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserVO findUserDetail(String loginType, String username,String companyNo) {
|
||||
UserVO userVO = null;
|
||||
//处理登录
|
||||
switch (loginType){
|
||||
case OauthConstant.USER_USERNAME:
|
||||
userVO = userFeign.usernameLogin(username,companyNo);
|
||||
if (!EmptyUtil.isNullOrEmpty(userVO)){
|
||||
userVO.setOnlyAuthenticate(false);
|
||||
}
|
||||
return userVO;
|
||||
case OauthConstant.CUSTOMER_USERNAME:
|
||||
userVO = customerFeign.usernameLogin(username,companyNo);
|
||||
if (!EmptyUtil.isNullOrEmpty(userVO)){
|
||||
userVO.setOnlyAuthenticate(true);
|
||||
}
|
||||
return userVO;
|
||||
default:
|
||||
throw new ProjectException(AuthEnum.LOGIN_FAIL);
|
||||
}
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package com.itheima.sfbx.security.handler.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.itheima.sfbx.framework.commons.constant.security.CompanyConstant;
|
||||
import com.itheima.sfbx.framework.commons.constant.security.OauthConstant;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.*;
|
||||
import com.itheima.sfbx.framework.commons.enums.security.AuthEnum;
|
||||
import com.itheima.sfbx.framework.commons.exception.ProjectException;
|
||||
import com.itheima.sfbx.framework.commons.utils.EmptyUtil;
|
||||
import com.itheima.sfbx.security.feign.CustomerFeign;
|
||||
import com.itheima.sfbx.security.feign.UserFeign;
|
||||
import com.itheima.sfbx.security.handler.CacheTokenHandler;
|
||||
import com.itheima.sfbx.security.handler.LoginAuthHandler;
|
||||
import com.itheima.sfbx.security.wechat.WechatService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @ClassName WechatLoginAuthHandlerImpl.java
|
||||
* @Description 微信登录处理
|
||||
*/
|
||||
@Component("wechatLoginAuthHandler")
|
||||
public class WechatLoginAuthHandlerImpl implements LoginAuthHandler {
|
||||
|
||||
|
||||
//调用RPC原创服务
|
||||
@Autowired
|
||||
UserFeign userFeign;
|
||||
|
||||
//调用RPC原创服务
|
||||
@Autowired
|
||||
CustomerFeign customerFeign;
|
||||
|
||||
@Autowired
|
||||
TokenEndpoint tokenEndpoint;
|
||||
|
||||
@Autowired
|
||||
CacheTokenHandler cacheTokenHandler;
|
||||
|
||||
@Autowired
|
||||
WechatService wechatService;
|
||||
|
||||
@Autowired
|
||||
BCryptPasswordEncoder bCryptPasswordEncoder;
|
||||
|
||||
@Override
|
||||
public UserVO loginHandler(Principal principal, Map<String, String> parameters,
|
||||
String loginBeanName, CompanyVO companyVO)
|
||||
throws HttpRequestMethodNotSupportedException {
|
||||
String code = parameters.get(OauthConstant.CODE_KEY);
|
||||
String clientId = parameters.get(OauthConstant.CLIENT_ID_KEY);
|
||||
String loginType = parameters.get(OauthConstant.LOGIN_TYPE_KEY);
|
||||
//code兑换openId
|
||||
AuthChannelVO authChannelVO = companyVO.getAuthChannelVOs().stream().filter(n -> {
|
||||
return n.getChannelLabel().equals(CompanyConstant.CHANNEL_LABEL_WECHAT);
|
||||
}).findFirst().get();
|
||||
//微信拿openId
|
||||
String openId = wechatService.openId(authChannelVO.getAppId(), authChannelVO.getAppSecret(), code);
|
||||
UsernameVO usernameVO = UsernameVO.builder()
|
||||
.username(openId)
|
||||
.clientId(clientId)
|
||||
.companyNo(companyVO.getCompanyNo())
|
||||
.loginType(loginType)
|
||||
.loginBeanName(loginBeanName)
|
||||
.build();
|
||||
String username = JSONObject.toJSONString(usernameVO);
|
||||
parameters.put(OauthConstant.USER_NAME_KEY,username);
|
||||
parameters.put(OauthConstant.PASSWORD_KEY,openId);
|
||||
OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
|
||||
return cacheTokenHandler.cacheToken(oAuth2AccessToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserVO findUserDetail(String loginType, String openId,String companyNo) {
|
||||
//处理登录
|
||||
UserVO userVO = null;
|
||||
String password = bCryptPasswordEncoder.encode(openId);
|
||||
switch (loginType){
|
||||
case OauthConstant.USER_WECHAT:
|
||||
userVO = userFeign.wechatLogin(openId,companyNo);
|
||||
if (!EmptyUtil.isNullOrEmpty(userVO)){
|
||||
userVO.setPassword(password);
|
||||
userVO.setOnlyAuthenticate(false);
|
||||
}
|
||||
return userVO;
|
||||
case OauthConstant.CUSTOMER_WECHAT:
|
||||
userVO = customerFeign.wechatLogin(openId,companyNo);
|
||||
//如果用户不存在则注册用户
|
||||
if (EmptyUtil.isNullOrEmpty(userVO)){
|
||||
CustomerVO customerVO = CustomerVO.builder()
|
||||
.username(openId)
|
||||
.openId(openId)
|
||||
.password(password)
|
||||
.build();
|
||||
return customerFeign.registerUser(customerVO);
|
||||
}
|
||||
userVO.setPassword(password);
|
||||
userVO.setOnlyAuthenticate(true);
|
||||
return userVO;
|
||||
default:
|
||||
throw new ProjectException(AuthEnum.LOGIN_FAIL);
|
||||
}
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
package com.itheima.sfbx.security.web;
|
||||
|
||||
import com.itheima.sfbx.framework.commons.basic.ResponseResult;
|
||||
import com.itheima.sfbx.framework.commons.constant.basic.SuperConstant;
|
||||
import com.itheima.sfbx.framework.commons.constant.security.OauthConstant;
|
||||
import com.itheima.sfbx.framework.commons.dto.security.UserVO;
|
||||
import com.itheima.sfbx.framework.commons.enums.basic.BaseEnum;
|
||||
import com.itheima.sfbx.framework.commons.utils.ResponseResultBuild;
|
||||
import com.itheima.sfbx.framework.commons.utils.SubjectContent;
|
||||
import com.itheima.sfbx.security.adepter.LoginAuthAdepter;
|
||||
import com.itheima.sfbx.security.handler.LogoutHandler;
|
||||
import com.itheima.sfbx.security.handler.RefreshTokenHandler;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.redisson.api.RBucket;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import springfox.documentation.annotations.ApiIgnore;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 自定义Oauth2获取令牌接口
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/oauth")
|
||||
@Api(tags = "登录认证")
|
||||
public class AuthController {
|
||||
|
||||
@Autowired
|
||||
private LoginAuthAdepter loginAuthAdepter;
|
||||
|
||||
@Autowired
|
||||
private LogoutHandler logoutHandler;
|
||||
|
||||
@Autowired
|
||||
private RefreshTokenHandler refreshTokenHandler;
|
||||
|
||||
/**
|
||||
* Oauth2登录认证
|
||||
* 登录必传:
|
||||
* client_id:客户端
|
||||
* client_secret:客户端秘钥
|
||||
* grant_type:登录传入:password
|
||||
* login_type:OauthConstant中选择:USER_USERNAME、USER_MOBILE、CUSTOMER_USERNAME、CUSTOMER_MOBILE
|
||||
* username:用户账号【用户名、手机号】
|
||||
* password:用户密码【明文密码、手机验证码】
|
||||
* code:三方授权登录传入的授权码
|
||||
* 刷新必传:
|
||||
* client_id:客户端
|
||||
* grant_type:refresh_token
|
||||
* client_secret:客户端秘钥
|
||||
* jti:本次回话标识
|
||||
*/
|
||||
@ApiOperation(value = "登录认证",notes = "登录认证")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "grant_type", value = "授权模式", example = "password",required = true),
|
||||
@ApiImplicitParam(name = "client_id", value = "Oauth2客户端ID",example = "operators-pc", required = true),
|
||||
@ApiImplicitParam(name = "client_secret",value = "Oauth2客户端秘钥", example = "pass", required = true),
|
||||
@ApiImplicitParam(name = "username", value = "登录用户名",example = "admin@qq.com"),
|
||||
@ApiImplicitParam(name = "mobile", value = "登录用户名", example = "15156403088"),
|
||||
@ApiImplicitParam(name = "usetToken",value = "会话usetToken",required = true),
|
||||
@ApiImplicitParam(name = "password", value = "登录密码", example = "pass"),
|
||||
@ApiImplicitParam(name = "login_type", value = "登录类型", example = "详解OauthConstant登录类型"),
|
||||
@ApiImplicitParam(name = "code", value = "小程序code")
|
||||
})
|
||||
@RequestMapping(value = "/token", method = RequestMethod.POST)
|
||||
public ResponseResult<UserVO> postAccessToken(@ApiIgnore Principal principal,
|
||||
@ApiIgnore @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
|
||||
UserVO userVO = null;
|
||||
boolean isRefreshToken = OauthConstant.GRANT_TYPE_REFRESH_TOKEN
|
||||
.equals(parameters.get(OauthConstant.GRANT_TYPE_KEY));
|
||||
//令牌刷新
|
||||
if (isRefreshToken){
|
||||
userVO =refreshTokenHandler.refreshToken(principal, parameters);
|
||||
//用户登录
|
||||
}else {
|
||||
userVO = loginAuthAdepter.adepterRoutes(principal, parameters);
|
||||
}
|
||||
return ResponseResultBuild.build(BaseEnum.SUCCEED, userVO);
|
||||
}
|
||||
|
||||
/***
|
||||
* @description 退出接口
|
||||
* @return
|
||||
*/
|
||||
@ApiOperation(value = "退出登录",notes = "退出登录")
|
||||
@RequestMapping(value = "/logout", method = RequestMethod.POST)
|
||||
public ResponseResult<Boolean> logout() {
|
||||
UserVO userVO = SubjectContent.getUserVO();
|
||||
Boolean flag = logoutHandler.logout(userVO);
|
||||
return ResponseResultBuild.build(BaseEnum.SUCCEED, flag);
|
||||
}
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.itheima.sfbx.security.web;
|
||||
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.RSAKey;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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 java.security.KeyPair;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Api(tags = "获取公钥")
|
||||
@RestController
|
||||
@RequestMapping("rsa")
|
||||
public class PublicKeyController {
|
||||
|
||||
@Autowired
|
||||
private KeyPair keyPair;
|
||||
|
||||
@ApiOperation(value = "获取公钥",notes = "获取公钥")
|
||||
@GetMapping("/public-key")
|
||||
public Map<String, Object> loadPublicKey() {
|
||||
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
|
||||
RSAKey key = new RSAKey.Builder(publicKey).build();
|
||||
Map<String, Object> map = new JWKSet(key).toJSONObject();
|
||||
log.info("========获得公钥的信息:{}===========",map.toString());
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.itheima.sfbx.security.wechat;
|
||||
|
||||
/**
|
||||
* @ClassName wechatServer.java
|
||||
* @Description 微信接口服务
|
||||
*/
|
||||
public interface WechatService {
|
||||
|
||||
/***
|
||||
* @description 查询用户openId
|
||||
* @param appId 应用
|
||||
* @param appSecret
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
String openId(String appId,String appSecret,String code);
|
||||
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.itheima.sfbx.security.wechat.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.itheima.sfbx.security.wechat.WechatService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @ClassName WechatServiceImpl.java
|
||||
* @Description TODO
|
||||
*/
|
||||
@Service
|
||||
public class WechatServiceImpl implements WechatService {
|
||||
|
||||
@Autowired
|
||||
RestTemplate restTemplate;
|
||||
|
||||
@Override
|
||||
public String openId(String appId, String appSecret, String code) {
|
||||
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+appId+
|
||||
"&secret="+appSecret+
|
||||
"&code="+code+
|
||||
"&grant_type=authorization_code" ;
|
||||
String wechatString = restTemplate.getForObject(url, String.class);
|
||||
JSONObject jsonObject = JSONObject.parseObject(wechatString);
|
||||
return jsonObject.getString("openid");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
_ __ __
|
||||
(_) /__________ ______/ /_
|
||||
/ / __/ ___/ __ `/ ___/ __/
|
||||
/ / /_/ /__/ /_/ (__ ) /_
|
||||
/_/\__/\___/\__,_/____/\__/
|
||||
:: Spring Boot :: (v-2.7.10)
|
||||
:: Spring Cloud :: (v-2021.0.6)
|
||||
:: Spring Cloud Alibaba :: (v-2021.0.1.0)
|
||||
:: Itheima Project Cloud :: (V-1.0.2-SNAPSHOT)
|
||||
:: 献给可爱的传智人 ::
|
||||
@@ -0,0 +1,45 @@
|
||||
#服务配置
|
||||
server:
|
||||
#端口
|
||||
port: 7078
|
||||
#服务编码
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
spring:
|
||||
config:
|
||||
activate:
|
||||
on-profile:
|
||||
- test
|
||||
main:
|
||||
allow-circular-references: true
|
||||
allow-bean-definition-overriding: true
|
||||
mvc:
|
||||
pathmatch:
|
||||
matching-strategy: ant_path_matcher
|
||||
#应用配置
|
||||
application:
|
||||
#应用名称
|
||||
name: security-oauth
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: ${NACOS_ADDRESS:nacos-service.yjy-public-sfbx-java.svc.cluster.local:20015} # nacos注册中心
|
||||
group: SEATA_GROUP
|
||||
service: ${spring.application.name}
|
||||
username: ${NACOS_USERNAME:nacos}
|
||||
password: ${NACOS_PASSWORD:PKsf*bxQ4;yP3a+}
|
||||
config:
|
||||
server-addr: ${NACOS_ADDRESS:nacos-service.yjy-public-sfbx-java.svc.cluster.local:20015} # nacos注册中心
|
||||
group: SEATA_GROUP
|
||||
file-extension: yml
|
||||
shared-configs: # 共享配置
|
||||
- data-id: shared-spring-task.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-redisson.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
username: ${NACOS_USERNAME:nacos}
|
||||
password: ${NACOS_PASSWORD:PKsf*bxQ4;yP3a+}
|
||||
logging:
|
||||
config: classpath:logback.xml
|
||||
@@ -0,0 +1,39 @@
|
||||
#服务配置
|
||||
server:
|
||||
#端口
|
||||
port: 7078
|
||||
#服务编码
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
main:
|
||||
allow-circular-references: true
|
||||
allow-bean-definition-overriding: true
|
||||
mvc:
|
||||
pathmatch:
|
||||
matching-strategy: ant_path_matcher
|
||||
#应用配置
|
||||
application:
|
||||
#应用名称
|
||||
name: security-oauth
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 192.168.12.129:8848 # nacos注册中心
|
||||
group: SEATA_GROUP
|
||||
service: ${spring.application.name}
|
||||
config:
|
||||
server-addr: 192.168.12.129:8848 # nacos配置中心地址
|
||||
group: SEATA_GROUP
|
||||
file-extension: yml
|
||||
shared-configs: # 共享配置
|
||||
- data-id: shared-spring-task.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
- data-id: shared-redisson.yml #配置文件名-DataId
|
||||
group: SEATA_GROUP
|
||||
refresh: false
|
||||
logging:
|
||||
config: classpath:logback.xml
|
||||
Binary file not shown.
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration debug="false">
|
||||
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 -->
|
||||
<property name="LOG_HOME" value="/data/logs/security-oauth" />
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<!-- 按照每天生成日志文件 -->
|
||||
<appender name="FILE"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!--日志文件输出的文件名 -->
|
||||
<FileNamePattern>${LOG_HOME}/security-oauth.log.%d{yyyy-MM-dd}.log
|
||||
</FileNamePattern>
|
||||
<!--日志文件保留天数 -->
|
||||
<MaxHistory>30</MaxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern>
|
||||
</encoder>
|
||||
<!--日志文件最大的大小 -->
|
||||
<triggeringPolicy
|
||||
class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
|
||||
<MaxFileSize>10MB</MaxFileSize>
|
||||
</triggeringPolicy>
|
||||
</appender>
|
||||
<!-- show parameters for hibernate sql 专为 Hibernate 定制 -->
|
||||
<logger name="org.hibernate.type.descriptor.sql.BasicBinder"
|
||||
level="TRACE" />
|
||||
<logger name="org.hibernate.type.descriptor.sql.BasicExtractor"
|
||||
level="DEBUG" />
|
||||
<logger name="org.hibernate.SQL" level="DEBUG" />
|
||||
<logger name="org.hibernate.engine.QueryParameters" level="DEBUG" />
|
||||
<logger name="org.hibernate.engine.query.HQLQueryPlan" level="DEBUG" />
|
||||
|
||||
<!--myibatis log configure -->
|
||||
<logger name="com.apache.ibatis" level="TRACE" />
|
||||
<logger name="java.sql.Connection" level="DEBUG" />
|
||||
<logger name="java.sql.Statement" level="DEBUG" />
|
||||
<logger name="java.sql.PreparedStatement" level="DEBUG" />
|
||||
|
||||
<!-- 日志输出级别 -->
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
<!--日志异步到数据库 -->
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user