feat: 初始化

This commit is contained in:
2024-04-09 11:34:46 +08:00
commit 39f3acc15f
3209 changed files with 253442 additions and 0 deletions
@@ -0,0 +1,27 @@
package com.yida.data.auth;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import cc.mrbird.febs.common.security.starter.annotation.EnableFebsCloudResourceServer;
/**
* @author MrBird
*/
@SpringBootApplication
@EnableRedisHttpSession
@EnableFebsCloudResourceServer
@MapperScan("com.yida.data.auth.mapper")
@EnableFeignClients(basePackages = "com.yida.data")
public class EduAuthApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(EduAuthApplication.class)
.web(WebApplicationType.SERVLET)
.run(args);
}
}
@@ -0,0 +1,124 @@
package com.yida.data.auth.configure;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter;
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.redis.RedisTokenStore;
import java.util.UUID;
import com.yida.data.auth.properties.FebsAuthProperties;
import com.yida.data.auth.service.impl.RedisAuthenticationCodeService;
import com.yida.data.auth.service.impl.RedisClientDetailsService;
import com.yida.data.auth.translator.FebsWebResponseExceptionTranslator;
import lombok.RequiredArgsConstructor;
/**
* 认证服务器配置
*/
@Configuration
@EnableAuthorizationServer
@RequiredArgsConstructor
public class FebsAuthorizationServerConfigure extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
private final UserDetailsService userDetailService;
private final PasswordEncoder passwordEncoder;
private final FebsWebResponseExceptionTranslator exceptionTranslator;
private final FebsAuthProperties properties;
private final RedisAuthenticationCodeService authenticationCodeService;
private final RedisClientDetailsService redisClientDetailsService;
private final RedisConnectionFactory redisConnectionFactory;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(redisClientDetailsService);
}
@Override
@SuppressWarnings("unchecked")
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenStore(tokenStore())
.userDetailsService(userDetailService)
.authorizationCodeServices(authenticationCodeService)
.authenticationManager(authenticationManager)
.exceptionTranslator(exceptionTranslator);
if (properties.getEnableJwt()) {
endpoints.accessTokenConverter(jwtAccessTokenConverter());
}
}
@Bean
public TokenStore tokenStore() {
if (properties.getEnableJwt()) {
return new JwtTokenStore(jwtAccessTokenConverter());
} else {
RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
// 解决每次生成的 token都一样的问题
redisTokenStore.setAuthenticationKeyGenerator(oAuth2Authentication -> UUID.randomUUID().toString());
return redisTokenStore;
}
}
@Bean
public TokenApprovalStore tokenApprovalStore(TokenStore tokenStore){
TokenApprovalStore tokenApprovalStore = new TokenApprovalStore();
tokenApprovalStore.setTokenStore(tokenStore);
return tokenApprovalStore;
}
@Bean
@Primary
public DefaultTokenServices defaultTokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(redisClientDetailsService);
return tokenServices;
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
DefaultAccessTokenConverter defaultAccessTokenConverter = (DefaultAccessTokenConverter) accessTokenConverter.getAccessTokenConverter();
DefaultUserAuthenticationConverter userAuthenticationConverter = new DefaultUserAuthenticationConverter();
userAuthenticationConverter.setUserDetailsService(userDetailService);
defaultAccessTokenConverter.setUserTokenConverter(userAuthenticationConverter);
accessTokenConverter.setSigningKey(properties.getJwtAccessKey());
return accessTokenConverter;
}
@Bean
public ResourceOwnerPasswordTokenGranter resourceOwnerPasswordTokenGranter(AuthenticationManager authenticationManager, OAuth2RequestFactory oAuth2RequestFactory) {
DefaultTokenServices defaultTokenServices = defaultTokenServices();
if (properties.getEnableJwt()) {
defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter());
}
return new ResourceOwnerPasswordTokenGranter(authenticationManager, defaultTokenServices, redisClientDetailsService, oAuth2RequestFactory);
}
@Bean
public DefaultOAuth2RequestFactory oAuth2RequestFactory() {
return new DefaultOAuth2RequestFactory(redisClientDetailsService);
}
}
@@ -0,0 +1,65 @@
package com.yida.data.auth.configure;
import com.yida.data.auth.handler.FebsWebLoginFailureHandler;
import com.yida.data.auth.handler.FebsWebLoginSuccessHandler;
import com.yida.data.auth.filter.ValidateCodeFilter;
import com.yida.data.common.core.entity.constant.EndpointConstant;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
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.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
* WebSecurity配置
*
* @author MrBird
*/
@Order(2)
@EnableWebSecurity
@RequiredArgsConstructor
public class FebsSecurityConfigure extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailService;
private final ValidateCodeFilter validateCodeFilter;
private final PasswordEncoder passwordEncoder;
private final FebsWebLoginSuccessHandler successHandler;
private final FebsWebLoginFailureHandler failureHandler;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
.requestMatchers()
.antMatchers(EndpointConstant.OAUTH_ALL, EndpointConstant.LOGIN)
.and()
.authorizeRequests()
.antMatchers(EndpointConstant.OAUTH_ALL).authenticated()
.and()
.formLogin()
.loginPage(EndpointConstant.LOGIN)
.loginProcessingUrl(EndpointConstant.LOGIN)
.successHandler(successHandler)
.failureHandler(failureHandler)
.permitAll()
.and().csrf().disable()
.httpBasic().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailService).passwordEncoder(passwordEncoder);
}
}
@@ -0,0 +1,97 @@
package com.yida.data.auth.controller;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import com.yida.data.auth.service.OauthClientDetailsService;
import com.yida.data.common.core.entity.FebsResponse;
import com.yida.data.common.core.entity.QueryRequest;
import com.yida.data.common.core.entity.auth.OauthClientDetails;
import com.yida.data.common.core.exception.FebsException;
import com.yida.data.common.core.utils.FebsUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* @author Yuuki
*/
@Slf4j
@Validated
@RestController
@RequiredArgsConstructor
@RequestMapping("client")
public class OauthClientDetailsController {
private final OauthClientDetailsService oauthClientDetailsService;
@GetMapping("check/{clientId}")
public boolean checkUserName(@NotBlank(message = "{required}") @PathVariable String clientId) {
OauthClientDetails client = this.oauthClientDetailsService.findById(clientId);
return client == null;
}
@GetMapping("secret/{clientId}")
@PreAuthorize("hasAuthority('client:decrypt')")
public FebsResponse getOriginClientSecret(@NotBlank(message = "{required}") @PathVariable String clientId) {
OauthClientDetails client = this.oauthClientDetailsService.findById(clientId);
String origin = client != null ? client.getOriginSecret() : StringUtils.EMPTY;
return new FebsResponse().data(origin);
}
@GetMapping
@PreAuthorize("hasAuthority('client:view')")
public FebsResponse oauthCliendetailsList(QueryRequest request, OauthClientDetails oAuthClientDetails) {
Map<String, Object> dataTable = FebsUtil.getDataTable(this.oauthClientDetailsService.findOauthClientDetails(request, oAuthClientDetails));
return new FebsResponse().data(dataTable);
}
@PostMapping
@PreAuthorize("hasAuthority('client:add')")
public void addOauthCliendetails(@Valid OauthClientDetails oAuthClientDetails) throws FebsException {
try {
this.oauthClientDetailsService.createOauthClientDetails(oAuthClientDetails);
} catch (Exception e) {
String message = "新增客户端失败";
log.error(message, e);
throw new FebsException(message);
}
}
@DeleteMapping
@PreAuthorize("hasAuthority('client:delete')")
public void deleteOauthCliendetails(@NotBlank(message = "{required}") String clientIds) throws FebsException {
try {
this.oauthClientDetailsService.deleteOauthClientDetails(clientIds);
} catch (Exception e) {
String message = "删除客户端失败";
log.error(message, e);
throw new FebsException(message);
}
}
@PutMapping
@PreAuthorize("hasAuthority('client:update')")
public void updateOauthCliendetails(@Valid OauthClientDetails oAuthClientDetails) throws FebsException {
try {
this.oauthClientDetailsService.updateOauthClientDetails(oAuthClientDetails);
} catch (Exception e) {
String message = "修改客户端失败";
log.error(message, e);
throw new FebsException(message);
}
}
}
@@ -0,0 +1,70 @@
package com.yida.data.auth.controller;
import com.yida.data.auth.manager.UserManager;
import com.yida.data.auth.service.SecurityService;
import com.yida.data.auth.service.ValidateCodeService;
import com.yida.data.common.core.entity.FebsResponse;
import com.yida.data.common.core.entity.constant.StringConstant;
import com.yida.data.common.core.exception.ValidateCodeException;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.Principal;
/**
* @author MrBird
*/
@Controller
@RequiredArgsConstructor
public class SecurityController {
private final ValidateCodeService validateCodeService;
private final SecurityService securityService;
private final UserManager userManager;
private final ConsumerTokenServices consumerTokenServices;
@ResponseBody
@GetMapping("user")
public Principal currentUser(Principal principal) {
return principal;
}
@ResponseBody
@GetMapping("captcha")
public void captcha(HttpServletRequest request, HttpServletResponse response) throws IOException, ValidateCodeException {
validateCodeService.create(request, response);
}
@RequestMapping("login")
public String login() {
return "login";
}
@RequestMapping("test")
public String test() {
return "test";
}
@ResponseBody
@DeleteMapping("signout")
public FebsResponse signout(HttpServletRequest request, @RequestHeader("Authorization") String token) {
token = StringUtils.replace(token, "bearer ", StringConstant.EMPTY);
consumerTokenServices.revokeToken(token);
return new FebsResponse().message("signout");
}
@ApiOperation("获取学校负责人登录信息")
@ResponseBody
@GetMapping("/inSchool")
public FebsResponse inSchool(Long schoolId) {
return new FebsResponse().data(securityService.getSchoolToken(schoolId));
}
}
@@ -0,0 +1,225 @@
package com.yida.data.auth.controller;
import com.yida.data.auth.entity.BindUser;
import com.yida.data.auth.service.SocialLoginService;
import com.yida.data.common.core.common.ModuleName;
import com.yida.data.common.core.common.ResultBean;
import com.yida.data.common.core.entity.FebsResponse;
import com.yida.data.common.core.entity.constant.StringConstant;
import com.yida.data.common.core.entity.system.UserConnection;
import com.yida.data.common.core.exception.FebsException;
import com.yida.data.common.core.utils.FebsUtil;
import com.yida.data.log.annotation.OperationLog;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* @author MrBird
*/
@Slf4j
@Controller
@RefreshScope
@RequiredArgsConstructor
@RequestMapping("social")
public class SocialLoginController {
private static final String TYPE_LOGIN = "login";
private static final String TYPE_BIND = "bind";
private final SocialLoginService socialLoginService;
@Value("${febs.frontUrl}")
private String frontUrl;
/**
* 登录
*
* @param oauthType 第三方登录类型
* @param response response
*/
@ResponseBody
@GetMapping("/login/{oauthType}/{type}")
public void renderAuth(@PathVariable String oauthType, @PathVariable String type, HttpServletResponse response)
throws IOException, FebsException {
AuthRequest authRequest = socialLoginService.renderAuth(oauthType);
response.sendRedirect(
authRequest.authorize(oauthType + StringConstant.DOUBLE_COLON + AuthStateUtils.createState()) + "::"
+ type);
}
/**
* 登录成功后的回调
*
* @param oauthType 第三方登录类型
* @param callback 携带返回的信息
* @return String
*/
@GetMapping("/{oauthType}/callback")
public String login(@PathVariable String oauthType, AuthCallback callback, String state, Model model) {
try {
FebsResponse febsResponse = null;
String type = StringUtils.substringAfterLast(state, StringConstant.DOUBLE_COLON);
if (StringUtils.equals(type, TYPE_BIND)) {
febsResponse = socialLoginService.resolveBind(oauthType, callback);
} else {
febsResponse = socialLoginService.resolveLogin(oauthType, callback);
}
model.addAttribute("response", febsResponse);
model.addAttribute("frontUrl", frontUrl);
return "result";
} catch (Exception e) {
String errorMessage = FebsUtil.containChinese(e.getMessage()) ? e.getMessage() : "第三方登录失败";
model.addAttribute("error", e.getMessage());
return "fail";
}
}
/**
* 绑定并登录
*
* @param bindUser bindUser
* @param authUser authUser
* @return FebsResponse
*/
@ResponseBody
@PostMapping("bind/login")
public FebsResponse bindLogin(@Valid BindUser bindUser, AuthUser authUser) throws FebsException {
OAuth2AccessToken oAuth2AccessToken = this.socialLoginService.bindLogin(bindUser, authUser);
return new FebsResponse().data(oAuth2AccessToken);
}
/**
* 注册并登录
*
* @param registUser registUser
* @param authUser authUser
* @return FebsResponse
*/
@ResponseBody
@PostMapping("sign/login")
public FebsResponse signLogin(@Valid BindUser registUser, AuthUser authUser) throws FebsException {
OAuth2AccessToken oAuth2AccessToken = this.socialLoginService.signLogin(registUser, authUser);
return new FebsResponse().data(oAuth2AccessToken);
}
/**
* 绑定
*
* @param bindUser bindUser
* @param authUser authUser
*/
@ResponseBody
@PostMapping("bind")
public void bind(BindUser bindUser, AuthUser authUser) throws FebsException {
this.socialLoginService.bind(bindUser, authUser);
}
/**
* 解绑
*
* @param bindUser bindUser
* @param oauthType oauthType
*/
@ResponseBody
@DeleteMapping("unbind")
public void unbind(BindUser bindUser, String oauthType) throws FebsException {
this.socialLoginService.unbind(bindUser, oauthType);
}
/**
* 根据用户名获取绑定关系
*
* @param username 用户名
* @return FebsResponse
*/
@ResponseBody
@GetMapping("connections/{username}")
public FebsResponse findUserConnections(@NotBlank(message = "{required}") @PathVariable String username) {
List<UserConnection> userConnections = this.socialLoginService.findUserConnections(username);
return new FebsResponse().data(userConnections);
}
/**
* 企业微信用户授权登录回调
*/
@ResponseBody
@GetMapping("qywx/login")
@OperationLog(module = ModuleName.AUTH, methods = "企业微信登录")
public FebsResponse qywxLogin(String code, String state, String corpId,
@ApiParam("0-教师,1-家长") Integer type) {
return new FebsResponse().data(socialLoginService.qywxLogin(code, state, corpId, type));
}
@ResponseBody
@GetMapping("app/login")
//@OperationLog(module = ModuleName.AUTH, methods = "app登录")
public FebsResponse appLogin(@ApiParam("区域idapp") String areaId,
@ApiParam("学校Id") Long schoolId,
@ApiParam("手机号") String mobile,
@ApiParam("app用户id") String userId,
@ApiParam("0-教师,1-家长") Integer type) {
return new FebsResponse().data(socialLoginService.appLogin(areaId, schoolId, mobile, userId, type));
}
@ResponseBody
@GetMapping("wxpublic/login")
//@OperationLog(module = ModuleName.AUTH, methods = "app登录")
public FebsResponse wxpublicLogin(String code, String appId,
@ApiParam("0-教师,1-家长") Integer type,
Long schoolId, String mobile) {
return new FebsResponse().data(socialLoginService.wxpublicLogin(code, appId, type, schoolId, mobile));
}
/**
* h5登录
*
* @param schoolId 学校Id
* @param mobile 手机号
* @param type 0-教师,1-家长
* @return com.yida.data.common.core.entity.FebsResponse
* @author ZYJ
* @date 2022/9/19 17:25
*/
@ResponseBody
@GetMapping("h5/login")
public FebsResponse h5Login(@ApiParam("学校Id") Long schoolId, @ApiParam("手机号") String mobile,
@ApiParam("0-教师,1-家长") Integer type) {
return new FebsResponse().data(socialLoginService.h5Login(schoolId, mobile, type));
}
@ResponseBody
@ApiOperation("检查openId状态")
@GetMapping("smart/checkOpenId")
public ResultBean<Map<String, Object>> checkOpenId(@RequestParam @ApiParam(value = "微信公众号授权code", required = true) String code,
@RequestParam @ApiParam(value = "学校id(链接上附带)", required = true) Long deptId) {
return ResultBean.buildSuccess(socialLoginService.checkOpenId(code, deptId));
}
@ResponseBody
@ApiOperation("智慧迎新学生端登录接口")
@GetMapping("wxPublic/welcome/login")
public FebsResponse wxPublicLogin(@RequestParam @ApiParam(value = "微信openId", required = true) String openId,
@RequestParam @ApiParam(value = "学校id(链接上附带)", required = true) Long deptId) {
return new FebsResponse().data(socialLoginService.wxPublicLogin(openId, deptId));
}
}
@@ -0,0 +1,37 @@
package com.yida.data.auth.controller.in;
import com.yida.data.auth.service.impl.RedisClientDetailsService;
import io.swagger.annotations.ApiOperation;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.Approval;
import org.springframework.security.oauth2.provider.approval.Approval.ApprovalStatus;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/in/security")
@RestController
@RequiredArgsConstructor
public class InSecurityController {
private final TokenApprovalStore tokenApprovalStore;
private final RedisClientDetailsService redisClientDetailsService;
@ApiOperation("撤销认证")
@GetMapping("/revokeApproval")
public void revokeApproval(String userName, String clientId) {
ClientDetails client = redisClientDetailsService.loadClientByClientId(clientId);
List<Approval> approvals = new ArrayList<Approval>();
for (String scope : client.getScope()) {
approvals.add(new Approval(userName, clientId, scope, new Date(), ApprovalStatus.APPROVED));
}
tokenApprovalStore.revokeApprovals(approvals);
}
}
@@ -0,0 +1,20 @@
package com.yida.data.auth.entity;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* @author MrBird
*/
@Data
public class BindUser implements Serializable {
private static final long serialVersionUID = -3890998115990166651L;
@NotBlank(message = "{required}")
private String bindUsername;
@NotBlank(message = "{required}")
private String bindPassword;
}
@@ -0,0 +1,65 @@
package com.yida.data.auth.filter;
import com.yida.data.auth.service.ValidateCodeService;
import com.yida.data.common.core.entity.FebsResponse;
import com.yida.data.common.core.entity.constant.EndpointConstant;
import com.yida.data.common.core.entity.constant.GrantTypeConstant;
import com.yida.data.common.core.entity.constant.ParamsConstant;
import com.yida.data.common.core.exception.ValidateCodeException;
import com.yida.data.common.core.utils.FebsUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Nonnull;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 验证码过滤器
*
* @author MrBird
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class ValidateCodeFilter extends OncePerRequestFilter {
private final ValidateCodeService validateCodeService;
@Override
protected void doFilterInternal(@Nonnull HttpServletRequest httpServletRequest, @Nonnull HttpServletResponse httpServletResponse,
@Nonnull FilterChain filterChain) throws ServletException, IOException {
String header = httpServletRequest.getHeader(HttpHeaders.AUTHORIZATION);
RequestMatcher matcher = new AntPathRequestMatcher(EndpointConstant.OAUTH_TOKEN, HttpMethod.POST.toString());
if (matcher.matches(httpServletRequest)
&& StringUtils.equalsIgnoreCase(httpServletRequest.getParameter(ParamsConstant.GRANT_TYPE), GrantTypeConstant.PASSWORD)) {
try {
validateCode(httpServletRequest);
filterChain.doFilter(httpServletRequest, httpServletResponse);
} catch (Exception e) {
FebsResponse febsResponse = new FebsResponse();
FebsUtil.makeFailureResponse(httpServletResponse, febsResponse.message(e.getMessage()));
log.error(e.getMessage(), e);
}
} else {
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
private void validateCode(HttpServletRequest httpServletRequest) throws ValidateCodeException {
String code = httpServletRequest.getParameter(ParamsConstant.VALIDATE_CODE_CODE);
String key = httpServletRequest.getParameter(ParamsConstant.VALIDATE_CODE_KEY);
validateCodeService.check(key, code);
}
}
@@ -0,0 +1,33 @@
package com.yida.data.auth.handler;
import com.yida.data.common.core.entity.FebsResponse;
import com.yida.data.common.core.utils.FebsUtil;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author MrBird
*/
@Component
public class FebsWebLoginFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException exception) throws IOException {
String message;
if (exception instanceof BadCredentialsException) {
message = "用户名或密码错误!";
} else if (exception instanceof LockedException) {
message = "用户已被锁定!";
} else {
message = "认证失败,请联系网站管理员!";
}
FebsResponse febsResponse = new FebsResponse().message(message);
FebsUtil.makeFailureResponse(httpServletResponse, febsResponse);
}
}
@@ -0,0 +1,53 @@
package com.yida.data.auth.handler;
import com.yida.data.common.core.entity.FebsResponse;
import com.yida.data.common.core.utils.FebsUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* @author MrBird
*/
@Slf4j
@Component
public class FebsWebLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
private final RequestCache requestCache = new HttpSessionRequestCache();
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
HttpSession session = request.getSession(false);
if (session != null) {
Object attribute = session.getAttribute("SPRING_SECURITY_SAVED_REQUEST");
log.info("跳转到登录页的地址为: {}", attribute);
}
if (FebsUtil.isAjaxRequest(request)) {
FebsResponse data = new FebsResponse();
if (savedRequest == null) {
FebsUtil.makeFailureResponse(response, data.message("请通过授权码模式跳转到该页面"));
return;
}
data.data(savedRequest.getRedirectUrl());
FebsUtil.makeSuccessResponse(response, data);
} else {
if (savedRequest == null) {
super.onAuthenticationSuccess(request, response, authentication);
return;
}
clearAuthenticationAttributes(request);
getRedirectStrategy().sendRedirect(request, response, savedRequest.getRedirectUrl());
}
}
}
@@ -0,0 +1,174 @@
package com.yida.data.auth.manager;
import cc.mrbird.febs.common.redis.service.RedisService;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yida.data.auth.mapper.EduUserLogoMapper;
import com.yida.data.auth.mapper.MenuMapper;
import com.yida.data.auth.mapper.RoleMapper;
import com.yida.data.auth.mapper.UserMapper;
import com.yida.data.auth.mapper.UserMenuMapper;
import com.yida.data.auth.mapper.UserRoleMapper;
import com.yida.data.common.core.entity.constant.CachePrefixConstant;
import com.yida.data.common.core.entity.constant.FebsConstant;
import com.yida.data.common.core.entity.constant.StringConstant;
import com.yida.data.common.core.entity.system.Dept;
import com.yida.data.common.core.entity.system.EduUserLogo;
import com.yida.data.common.core.entity.system.Menu;
import com.yida.data.common.core.entity.system.Role;
import com.yida.data.common.core.entity.system.SystemUser;
import com.yida.data.common.core.entity.system.SystemUserMenu;
import com.yida.data.common.core.entity.system.UserRole;
import com.yida.data.common.service.CommonService;
import com.yida.data.user.feign.RemoteTeacherService;
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* 用户业务逻辑
*
* @author MrBird
*/
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class UserManager {
private final UserMapper userMapper;
private final EduUserLogoMapper eduUserLogoMapper;
private final MenuMapper menuMapper;
private final RoleMapper roleMapper;
private final UserRoleMapper userRoleMapper;
private final UserMenuMapper userMenuMapper;
private final RemoteTeacherService remoteTeacherService;
private final RedisService redisService;
private final CommonService commonService;
/**
* 通过用户名查询用户信息
*
* @param username 用户名
* @return 用户
*/
public SystemUser findByName(String username) {
Object deptO = redisService.hget(CachePrefixConstant.USER_LOGIN_DEPT, username);
SystemUser user = userMapper.findByName(username);
if (user != null) {
if (deptO != null) {
Long deptId = Long.valueOf(deptO.toString());
Dept dept = commonService.getDept(deptId);
user.setDeptId(dept.getDeptId());
user.setDeptName(dept.getDeptName());
user.setDeptType(dept.getDeptType());
}
user.setUserLogo(
eduUserLogoMapper.selectOne(Wrappers.<EduUserLogo>lambdaQuery().eq(EduUserLogo::getUserId, user.getUserId())));
}
return user;
}
/**
* 通过用户名查询用户权限串
*
* @param username 用户名
* @return 权限
*/
public List<String> findUserPermissions(String username) {
// 获取角色权限
List<String> menus = menuMapper.findUserPermissions(username);
// 获取用户权限
List<SystemUserMenu> userPermissions = userMenuMapper.findUserPermissions(username);
// 过滤
if (CollUtil.isNotEmpty(userPermissions)) {
menus.addAll(
userPermissions.stream().filter(x -> x.getType() == 0).map(SystemUserMenu::getPerms).filter(StrUtil::isNotBlank)
.collect(Collectors.toList()));
List<String> excludePerms = userPermissions.stream().filter(x -> x.getType() == 1).map(SystemUserMenu::getPerms)
.filter(StrUtil::isNotBlank).collect(Collectors.toList());
if (CollUtil.isNotEmpty(excludePerms)) {
menus = menus.stream().filter(x -> !excludePerms.contains(x)).collect(Collectors.toList());
}
}
// 只返回串
return menus;
}
/**
* 注册用户
*
* @param username username
* @param password password
* @return SystemUser SystemUser
*/
@Transactional(rollbackFor = Exception.class)
public SystemUser registUser(String username, String password) {
SystemUser systemUser = new SystemUser();
systemUser.setUsername(username);
systemUser.setPassword(password);
systemUser.setStatus(SystemUser.STATUS_VALID);
systemUser.setSex(SystemUser.SEX_UNKNOW);
systemUser.setAvatar(SystemUser.DEFAULT_AVATAR);
systemUser.setDescription("注册用户");
userMapper.insert(systemUser);
UserRole userRole = new UserRole();
userRole.setUserId(systemUser.getUserId());
// 注册用户角色 ID
userRole.setRoleId(FebsConstant.REGISTER_ROLE_ID);
userRoleMapper.insert(userRole);
return systemUser;
}
/**
* 注册用户
*/
@Transactional(rollbackFor = Exception.class)
public SystemUser registUser(SystemUser systemUser) {
systemUser.setStatus(SystemUser.STATUS_VALID);
userMapper.insert(systemUser);
UserRole userRole = new UserRole();
userRole.setUserId(systemUser.getUserId());
if (systemUser.getRoleId() != null) {
// 注册用户角色 ID
userRole.setRoleId(Long.valueOf(systemUser.getRoleId()));
userRoleMapper.insert(userRole);
} else if (StrUtil.isNotBlank(systemUser.getRoleName())) {
Role role = roleMapper.selectOne(Wrappers.<Role>lambdaQuery().eq(Role::getRoleName, systemUser.getRoleName()));
userRole.setRoleId(role.getRoleId());
userRoleMapper.insert(userRole);
}
return systemUser;
}
/**
* 添加角色
*/
public void addRole(Long userId, Long roleId) {
UserRole userRole = new UserRole();
userRole.setRoleId(roleId);
userRole.setUserId(userId);
userRoleMapper.insert(userRole);
}
/**
* 查询主角色code字符串
*
* @param mainDeptId 主部门id
* @return java.lang.String
* @author ZYJ
* @date 2022/1/14 10:36
*/
public String findMainRole(Long mainDeptId) {
// 查询主角色code
List<String> rolePerms = userMapper.findMainRole(mainDeptId);
return rolePerms.stream().filter(StringUtils::isNotBlank).distinct().collect(Collectors.joining(StringConstant.COMMA));
}
}
@@ -0,0 +1,7 @@
package com.yida.data.auth.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yida.data.common.core.entity.system.EduUserApp;
public interface EduUserAppMapper extends BaseMapper<EduUserApp> {
}
@@ -0,0 +1,8 @@
package com.yida.data.auth.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yida.data.common.core.entity.system.EduUserLogo;
public interface EduUserLogoMapper extends BaseMapper<EduUserLogo> {
}
@@ -0,0 +1,20 @@
package com.yida.data.auth.mapper;
import com.yida.data.common.core.entity.system.Menu;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
/**
* @author MrBird
*/
public interface MenuMapper extends BaseMapper<Menu> {
/**
* 获取用户权限集
*
* @param username 用户名
* @return 权限集合
*/
List<String> findUserPermissions(String username);
}
@@ -0,0 +1,11 @@
package com.yida.data.auth.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yida.data.common.core.entity.auth.OauthClientDetails;
/**
* @author Yuuki
*/
public interface OauthClientDetailsMapper extends BaseMapper<OauthClientDetails> {
}
@@ -0,0 +1,8 @@
package com.yida.data.auth.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yida.data.common.core.entity.system.Role;
public interface RoleMapper extends BaseMapper<Role> {
}
@@ -0,0 +1,12 @@
package com.yida.data.auth.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yida.data.common.core.entity.system.UserConnection;
/**
* @author MrBird
*/
public interface UserConnectionMapper extends BaseMapper<UserConnection> {
}
@@ -0,0 +1,51 @@
package com.yida.data.auth.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yida.data.common.core.entity.system.SystemUser;
import com.yida.data.common.core.entity.system.UserDataPermission;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author MrBird
*/
public interface UserMapper extends BaseMapper<SystemUser> {
/**
* 获取用户
*
* @param username 用户名
* @return 用户
*/
SystemUser findByName(String username);
/**
* 获取用户数据权限
*
* @param userId 用户id
* @return 数据权限
*/
List<UserDataPermission> findUserDataPermissions(Long userId);
/**
* 查询用户
*
* @param deptId 部门
* @param perms 角色perms
* @return
*/
List<SystemUser> listUserByPerms(@Param("deptId") Long deptId,
@Param("perms") String perms);
/**
* 查询主角色code字符串
*
* @param mainDeptId 主部门id
* @return java.util.List<java.lang.String>
* @author ZYJ
* @date 2022/1/14 13:24
*/
List<String> findMainRole(@Param("mainDeptId") Long mainDeptId);
}
@@ -0,0 +1,26 @@
package com.yida.data.auth.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yida.data.common.core.entity.system.Menu;
import com.yida.data.common.core.entity.system.SystemUserMenu;
import java.util.List;
/**
* 用户权限dao曾
*
* @author ZYJ
* @date 2021/11/8
*/
public interface UserMenuMapper extends BaseMapper<SystemUserMenu> {
/**
* 通过用户名查询用户权限串
*
* @param username 用户名
* @return java.util.List<com.yida.data.common.core.entity.system.Menu>
* @author ZYJ
* @date 2021/11/8 15:54
*/
List<SystemUserMenu> findUserPermissions(String username);
}
@@ -0,0 +1,11 @@
package com.yida.data.auth.mapper;
import com.yida.data.common.core.entity.system.UserRole;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author MrBird
*/
public interface UserRoleMapper extends BaseMapper<UserRole> {
}
@@ -0,0 +1,35 @@
package com.yida.data.auth.properties;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import lombok.Data;
/**
* @author MrBird
*/
@Data
@SpringBootConfiguration
@PropertySource(value = {"classpath:febs-auth.properties"})
@ConfigurationProperties(prefix = "febs.auth")
public class FebsAuthProperties {
/**
* 验证码配置
*/
private FebsValidateCodeProperties code = new FebsValidateCodeProperties();
/**
* JWT加签密钥
*/
private String jwtAccessKey;
/**
* 是否使用 JWT令牌
*/
private Boolean enableJwt;
/**
* 社交登录所使用的 Client
*/
private String socialLoginClientId;
}
@@ -0,0 +1,39 @@
package com.yida.data.auth.properties;
import com.yida.data.common.core.entity.constant.ImageTypeConstant;
import lombok.Data;
/**
* @author MrBird
*/
@Data
public class FebsValidateCodeProperties {
/**
* 验证码有效时间,单位秒
*/
private Long time = 120L;
/**
* 验证码类型,可选值 png和 gif
*/
private String type = ImageTypeConstant.PNG;
/**
* 图片宽度,px
*/
private Integer width = 130;
/**
* 图片高度,px
*/
private Integer height = 48;
/**
* 验证码位数
*/
private Integer length = 4;
/**
* 验证码值的类型
* 1. 数字加字母
* 2. 纯数字
* 3. 纯字母
*/
private Integer charType = 2;
}
@@ -0,0 +1,53 @@
package com.yida.data.auth.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yida.data.common.core.entity.QueryRequest;
import com.yida.data.common.core.entity.auth.OauthClientDetails;
import com.yida.data.common.core.exception.FebsException;
/**
* @author Yuuki
*/
public interface OauthClientDetailsService extends IService<OauthClientDetails> {
/**
* 查询(分页)
*
* @param request QueryRequest
* @param oauthClientDetails oauthClientDetails
* @return IPage<OauthClientDetails>
*/
IPage<OauthClientDetails> findOauthClientDetails(QueryRequest request, OauthClientDetails oauthClientDetails);
/**
* 根据主键查询
*
* @param clientId clientId
* @return OauthClientDetails
*/
OauthClientDetails findById(String clientId);
/**
* 新增
*
* @param oauthClientDetails oauthClientDetails
* @throws FebsException FebsException
*/
void createOauthClientDetails(OauthClientDetails oauthClientDetails) throws FebsException;
/**
* 修改
*
* @param oauthClientDetails oauthClientDetails
*/
void updateOauthClientDetails(OauthClientDetails oauthClientDetails);
/**
* 删除
*
* @param clientIds clientIds
*/
void deleteOauthClientDetails(String clientIds);
}
@@ -0,0 +1,14 @@
package com.yida.data.auth.service;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
public interface SecurityService {
/**
* 得到学校管理员账号的token
*
* @param schoolId
* @return
*/
OAuth2AccessToken getSchoolToken(Long schoolId);
}
@@ -0,0 +1,130 @@
package com.yida.data.auth.service;
import com.yida.data.auth.entity.BindUser;
import com.yida.data.common.core.entity.FebsResponse;
import com.yida.data.common.core.entity.system.UserConnection;
import com.yida.data.common.core.exception.FebsException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import java.util.List;
import java.util.Map;
/**
* @author MrBird
*/
public interface SocialLoginService {
/**
* 解析第三方登录请求
*
* @param oauthType 第三方平台类型
* @return AuthRequest
* @throws FebsException 异常
*/
AuthRequest renderAuth(String oauthType) throws FebsException;
/**
* 处理第三方登录(绑定页面)
*
* @param oauthType 第三方平台类型
* @param callback 回调
* @return FebsResponse
* @throws FebsException 异常
*/
FebsResponse resolveBind(String oauthType, AuthCallback callback) throws FebsException;
/**
* 处理第三方登录(登录页面)
*
* @param oauthType 第三方平台类型
* @param callback 回调
* @return FebsResponse
* @throws FebsException 异常
*/
FebsResponse resolveLogin(String oauthType, AuthCallback callback) throws FebsException;
/**
* 绑定并登录
*
* @param bindUser 绑定用户
* @param authUser 第三方平台对象
* @return OAuth2AccessToken 令牌对象
* @throws FebsException 异常
*/
OAuth2AccessToken bindLogin(BindUser bindUser, AuthUser authUser) throws FebsException;
/**
* 注册并登录
*
* @param registUser 注册用户
* @param authUser 第三方平台对象
* @return OAuth2AccessToken 令牌对象
* @throws FebsException 异常
*/
OAuth2AccessToken signLogin(BindUser registUser, AuthUser authUser) throws FebsException;
/**
* 绑定
*
* @param bindUser 绑定对象
* @param authUser 第三方平台对象
* @throws FebsException 异常
*/
void bind(BindUser bindUser, AuthUser authUser) throws FebsException;
/**
* 解绑
*
* @param bindUser 绑定对象
* @param oauthType 第三方平台对象
* @throws FebsException 异常
*/
void unbind(BindUser bindUser, String oauthType) throws FebsException;
/**
* 根据用户名获取绑定关系
*
* @param username 用户名
* @return 绑定关系集合
*/
List<UserConnection> findUserConnections(String username);
/**
* 企业微信用户登录/注册登录
*/
Map<String, Object> qywxLogin(String code, String state, String corpId, Integer type);
/**
* app用户登录
*/
Map<String, Object> appLogin(String areaId, Long schoolId, String mobile, String userId, Integer type);
Map<String, Object> wxpublicLogin(String code, String appId, Integer type, Long schoolId, String mobile);
Map<String, Object> h5Login(Long schoolId, String mobile, Integer type);
/**
* 检查openId状态
*
* @param code 微信公众号授权code
* @param deptId 学校id
* @return java.util.Map<java.lang.String, java.lang.Object>
* @author ZYJ
* @date 2023/6/2 10:31
*/
Map<String, Object> checkOpenId(String code, Long deptId);
/**
* 迎新功能微信公众号登录
*
* @param openId 微信openId
* @param deptId 学校id
* @return java.util.Map<java.lang.String, java.lang.Object>
* @author ZYJ
* @date 2023/5/18 11:09
*/
Map<String, Object> wxPublicLogin(String openId, Long deptId);
}
@@ -0,0 +1,46 @@
package com.yida.data.auth.service;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
import com.yida.data.common.core.entity.system.UserConnection;
/**
* @author MrBird
*/
public interface UserConnectionService extends IService<UserConnection> {
/**
* 根据条件查询关联关系
*
* @param providerName 平台名称
* @param providerUserId 平台用户ID
* @return 关联关系
*/
UserConnection selectByCondition(String providerName, String providerUserId);
/**
* 根据条件查询关联关系
*
* @param username 用户名
* @return 关联关系
*/
List<UserConnection> selectByCondition(String username);
/**
* 新增
*
* @param userConnection userConnection
*/
void createUserConnection(UserConnection userConnection);
/**
* 删除
*
* @param username username 用户名
* @param providerName providerName 平台名称
*/
void deleteByCondition(String username, String providerName);
}
@@ -0,0 +1,32 @@
package com.yida.data.auth.service;
import com.yida.data.common.core.exception.ValidateCodeException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author MrBird
*/
public interface ValidateCodeService {
/**
* 生成验证码
*
* @param request HttpServletRequest
* @param response HttpServletResponse
* @throws IOException IO异常
* @throws ValidateCodeException 验证码异常
*/
void create(HttpServletRequest request, HttpServletResponse response) throws IOException, ValidateCodeException;
/**
* 校验验证码
*
* @param key 前端上送 key
* @param value 前端上送待校验值
* @throws ValidateCodeException 验证码异常
*/
void check(String key, String value) throws ValidateCodeException;
}
@@ -0,0 +1,95 @@
package com.yida.data.auth.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.yida.data.auth.manager.UserManager;
import cc.mrbird.febs.common.redis.service.RedisService;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.yida.data.common.core.entity.FebsAuthUser;
import com.yida.data.common.core.entity.constant.FebsConstant;
import com.yida.data.common.core.entity.constant.ParamsConstant;
import com.yida.data.common.core.entity.constant.SocialConstant;
import com.yida.data.common.core.entity.constant.StringConstant;
import com.yida.data.common.core.entity.system.SystemUser;
import com.yida.data.common.core.utils.FebsUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.security.core.GrantedAuthority;
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.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @author MrBird
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class FebsUserDetailServiceImpl implements UserDetailsService {
private final PasswordEncoder passwordEncoder;
private final UserManager userManager;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
HttpServletRequest httpServletRequest = FebsUtil.getHttpServletRequest();
SystemUser systemUser = userManager.findByName(username);
if (systemUser != null) {
List<String> permissions = userManager.findUserPermissions(systemUser.getUsername());
boolean notLocked = false;
if (StringUtils.equals(SystemUser.STATUS_VALID, systemUser.getStatus())) {
notLocked = true;
}
String password = systemUser.getPassword();
String loginType = (String) httpServletRequest.getAttribute(ParamsConstant.LOGIN_TYPE);
if (StringUtils.equals(loginType, SocialConstant.SOCIAL_LOGIN)) {
password = passwordEncoder.encode(SocialConstant.getSocialLoginPassword());
}
List<GrantedAuthority> grantedAuthorities = AuthorityUtils.NO_AUTHORITIES;
// 设置角色权限
if (StringUtils.isNotBlank(systemUser.getRolePerms())) {
List<String> collect = Arrays.stream(systemUser.getRolePerms().split(StringConstant.COMMA))
.map(s -> FebsConstant.SECURITY_ROLE_PREFIX + s)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(collect)) {
grantedAuthorities = AuthorityUtils
.commaSeparatedStringToAuthorityList(String.join(StringConstant.COMMA, collect));
}
}
// 设置菜单权限
if (CollUtil.isNotEmpty(permissions)) {
grantedAuthorities.addAll(AuthorityUtils.createAuthorityList(permissions.toArray(new String[0])));
}
// 查询主角色信息
if (Objects.nonNull(systemUser.getMainDeptId())) {
String role = this.userManager.findMainRole(systemUser.getMainDeptId());
systemUser.setMainRolePerms(role);
}
//查询登录所属部门:学校对应的
log.info("登陆用户信息sysUser: {}", systemUser);
log.info("登陆权限数据permission: {}", grantedAuthorities);
log.info("登陆主角色数据mainRole: {}", systemUser.getMainRolePerms());
FebsAuthUser authUser = new FebsAuthUser(systemUser.getUsername(), password, true, true, true, notLocked,
grantedAuthorities);
BeanUtils.copyProperties(systemUser, authUser);
return authUser;
} else {
throw new UsernameNotFoundException("");
}
}
}
@@ -0,0 +1,111 @@
package com.yida.data.auth.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.yida.data.auth.mapper.OauthClientDetailsMapper;
import com.yida.data.auth.service.OauthClientDetailsService;
import com.yida.data.common.core.entity.QueryRequest;
import com.yida.data.common.core.entity.auth.OauthClientDetails;
import com.yida.data.common.core.entity.constant.StringConstant;
import com.yida.data.common.core.exception.FebsException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* @author Yuuki
*/
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class OauthClientDetailsServiceImpl extends ServiceImpl<OauthClientDetailsMapper, OauthClientDetails> implements OauthClientDetailsService {
private final PasswordEncoder passwordEncoder;
private final RedisClientDetailsService redisClientDetailsService;
@Override
public IPage<OauthClientDetails> findOauthClientDetails(QueryRequest request, OauthClientDetails oauthClientDetails) {
LambdaQueryWrapper<OauthClientDetails> queryWrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(oauthClientDetails.getClientId())) {
queryWrapper.eq(OauthClientDetails::getClientId, oauthClientDetails.getClientId());
}
Page<OauthClientDetails> page = new Page<>(request.getPageNum(), request.getPageSize());
IPage<OauthClientDetails> result = this.page(page, queryWrapper);
List<OauthClientDetails> records = new ArrayList<>();
result.getRecords().forEach(o -> {
o.setOriginSecret(null);
o.setClientSecret(null);
records.add(o);
});
result.setRecords(records);
return result;
}
@Override
public OauthClientDetails findById(String clientId) {
return this.baseMapper.selectById(clientId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void createOauthClientDetails(OauthClientDetails oauthClientDetails) throws FebsException {
OauthClientDetails byId = this.findById(oauthClientDetails.getClientId());
if (byId != null) {
throw new FebsException("该Client已存在");
}
oauthClientDetails.setOriginSecret(oauthClientDetails.getClientSecret());
oauthClientDetails.setClientSecret(passwordEncoder.encode(oauthClientDetails.getClientSecret()));
boolean saved = this.save(oauthClientDetails);
if (saved) {
log.info("缓存Client -> {}", oauthClientDetails);
this.redisClientDetailsService.loadClientByClientId(oauthClientDetails.getClientId());
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateOauthClientDetails(OauthClientDetails oauthClientDetails) {
String clientId = oauthClientDetails.getClientId();
LambdaQueryWrapper<OauthClientDetails> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OauthClientDetails::getClientId, oauthClientDetails.getClientId());
oauthClientDetails.setClientId(null);
oauthClientDetails.setClientSecret(null);
boolean updated = this.update(oauthClientDetails, queryWrapper);
if (updated) {
log.info("更新Client -> {}", oauthClientDetails);
this.redisClientDetailsService.removeRedisCache(clientId);
this.redisClientDetailsService.loadClientByClientId(clientId);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteOauthClientDetails(String clientIds) {
Object[] clientIdArray = StringUtils.splitByWholeSeparatorPreserveAllTokens(clientIds, StringConstant.COMMA);
LambdaQueryWrapper<OauthClientDetails> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(OauthClientDetails::getClientId, clientIdArray);
boolean removed = this.remove(queryWrapper);
if (removed) {
log.info("删除ClientId为({})的Client", clientIds);
Arrays.stream(clientIdArray).forEach(c -> this.redisClientDetailsService.removeRedisCache(String.valueOf(c)));
}
}
}
@@ -0,0 +1,71 @@
package com.yida.data.auth.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.SerializationUtils;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.nio.charset.StandardCharsets;
/**
* 授权码保存到Redis,以确保认证服务器集群的一致性
*
* @author MrBird
*/
@Slf4j
@Service
public class RedisAuthenticationCodeService extends RandomValueAuthorizationCodeServices {
private static final String AUTH_CODE_KEY = "auth_code";
private final RedisConnectionFactory connectionFactory;
public RedisAuthenticationCodeService(RedisConnectionFactory connectionFactory) {
Assert.notNull(connectionFactory, "RedisConnectionFactory required");
this.connectionFactory = connectionFactory;
}
@Override
protected OAuth2Authentication remove(String code) {
RedisConnection conn = getConnection();
try {
byte[] bytes = conn.hGet(AUTH_CODE_KEY.getBytes(StandardCharsets.UTF_8), code.getBytes(StandardCharsets.UTF_8));
if (bytes == null || ArrayUtils.isEmpty(bytes)) {
return null;
}
OAuth2Authentication authentication = SerializationUtils.deserialize(bytes);
if (null != authentication) {
conn.hDel(AUTH_CODE_KEY.getBytes(StandardCharsets.UTF_8), code.getBytes(StandardCharsets.UTF_8));
}
return authentication;
} catch (Exception e) {
return null;
} finally {
conn.close();
}
}
@Override
protected void store(String code, OAuth2Authentication authentication) {
RedisConnection conn = getConnection();
try {
conn.hSet(AUTH_CODE_KEY.getBytes(StandardCharsets.UTF_8), code.getBytes(StandardCharsets.UTF_8),
SerializationUtils.serialize(authentication));
log.info("保存authentication code: {}至redis", code);
} catch (Exception e) {
log.error("保存authentication code至redis失败", e);
} finally {
conn.close();
}
}
private RedisConnection getConnection() {
return connectionFactory.getConnection();
}
}
@@ -0,0 +1,101 @@
package com.yida.data.auth.service.impl;
import cc.mrbird.febs.common.redis.service.RedisService;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.stereotype.Service;
import javax.sql.DataSource;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author Yuuki
*/
@Slf4j
@Service
public class RedisClientDetailsService extends JdbcClientDetailsService {
/**
* 缓存 client的 redis key,这里是 hash结构存储
*/
private static final String CACHE_CLIENT_KEY = "client_details";
private final RedisService redisService;
public RedisClientDetailsService(DataSource dataSource, RedisService redisService) {
super(dataSource);
this.redisService = redisService;
}
@Override
public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException {
ClientDetails clientDetails = null;
String value = (String) redisService.hget(CACHE_CLIENT_KEY, clientId);
if (StringUtils.isBlank(value)) {
clientDetails = cacheAndGetClient(clientId);
} else {
clientDetails = JSONObject.parseObject(value, BaseClientDetails.class);
}
return clientDetails;
}
/**
* 缓存 client并返回 client
*
* @param clientId clientId
*/
public ClientDetails cacheAndGetClient(String clientId) {
ClientDetails clientDetails = null;
clientDetails = super.loadClientByClientId(clientId);
if (clientDetails != null) {
BaseClientDetails baseClientDetails = (BaseClientDetails) clientDetails;
Set<String> autoApproveScopes = baseClientDetails.getAutoApproveScopes();
if (CollectionUtils.isNotEmpty(autoApproveScopes)) {
baseClientDetails.setAutoApproveScopes(
autoApproveScopes.stream().map(this::convert).collect(Collectors.toSet())
);
}
redisService.hset(CACHE_CLIENT_KEY, clientId, JSONObject.toJSONString(baseClientDetails));
}
return clientDetails;
}
/**
* 删除 redis缓存
*
* @param clientId clientId
*/
public void removeRedisCache(String clientId) {
redisService.hdel(CACHE_CLIENT_KEY, clientId);
}
/**
* 将 oauth_client_details全表刷入 redis
*/
public void loadAllClientToCache() {
if (redisService.hasKey(CACHE_CLIENT_KEY)) {
return;
}
log.info("将oauth_client_details全表刷入redis");
List<ClientDetails> list = super.listClientDetails();
if (CollectionUtils.isEmpty(list)) {
log.error("oauth_client_details表数据为空,请检查");
return;
}
list.forEach(client -> redisService.hset(CACHE_CLIENT_KEY, client.getClientId(), JSONObject.toJSONString(client)));
}
private String convert(String value) {
final String logicTrue = "1";
return logicTrue.equals(value) ? Boolean.TRUE.toString() : Boolean.FALSE.toString();
}
}
@@ -0,0 +1,32 @@
package com.yida.data.auth.service.impl;
import com.yida.data.common.core.entity.system.SystemUser;
import com.yida.data.common.core.entity.system.enums.RoleEnum;
import com.yida.data.common.core.utils.Asserts;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.stereotype.Service;
import java.util.List;
import com.yida.data.auth.mapper.UserMapper;
import com.yida.data.auth.service.SecurityService;
import com.yida.data.auth.util.SecurityUtil;
import cn.hutool.core.collection.CollUtil;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class SecurityServiceImpl implements SecurityService {
private final UserMapper userMapper;
private final SecurityUtil securityUtil;
@Override
public OAuth2AccessToken getSchoolToken(Long schoolId) {
List<SystemUser> userList = userMapper.listUserByPerms(schoolId, RoleEnum.ROLE_SCHOOL_PRINCIPAL.getValue());
Asserts.isTrue(CollUtil.isNotEmpty(userList), "该学校无学校负责人,请创建后重试");
SystemUser schoolUser = userList.get(0);
return securityUtil.getOauth2AccessToken(schoolUser);
}
}
@@ -0,0 +1,622 @@
package com.yida.data.auth.service.impl;
import cc.mrbird.febs.common.redis.service.RedisService;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xkcoding.justauth.AuthRequestFactory;
import com.yida.data.auth.entity.BindUser;
import com.yida.data.auth.manager.UserManager;
import com.yida.data.auth.mapper.RoleMapper;
import com.yida.data.auth.properties.FebsAuthProperties;
import com.yida.data.auth.service.SocialLoginService;
import com.yida.data.auth.service.UserConnectionService;
import com.yida.data.common.core.entity.FebsResponse;
import com.yida.data.common.core.entity.constant.*;
import com.yida.data.common.core.entity.smart.EduSmartWelcomeGuideRosterRelation;
import com.yida.data.common.core.entity.smart.EduSmartWelcomeGuideStep;
import com.yida.data.common.core.entity.system.*;
import com.yida.data.common.core.entity.user.EduStaff;
import com.yida.data.common.core.enums.RoleName;
import com.yida.data.common.core.exception.FebsException;
import com.yida.data.common.core.utils.Asserts;
import com.yida.data.common.core.utils.FebsUtil;
import com.yida.data.common.core.utils.WxPublicUtil;
import com.yida.data.common.core.utils.WxUtil;
import com.yida.data.common.service.CommonService;
import com.yida.data.school.feign.smart.RemoteSmartWelcomeService;
import com.yida.data.system.feign.RemoteDeptService;
import com.yida.data.system.feign.RemoteRoleService;
import com.yida.data.system.feign.RemoteUserService;
import com.yida.data.user.feign.RemoteStaffService;
import com.yida.data.user.feign.RemoteStudentApplyService;
import com.yida.data.user.feign.RemoteStudentService;
import com.yida.data.user.feign.RemoteTeacherService;
import com.yida.data.user.vo.ParentInfoVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @author MrBird
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class SocialLoginServiceImpl implements SocialLoginService {
private static final String USERNAME = "username";
private static final String PASSWORD = "password";
private static final String NOT_BIND = "not_bind";
private static final String SOCIAL_LOGIN_SUCCESS = "social_login_success";
private final UserManager userManager;
private final RoleMapper roleMapper;
private final AuthRequestFactory factory;
private final FebsAuthProperties properties;
private final PasswordEncoder passwordEncoder;
private final UserConnectionService userConnectionService;
private final ResourceOwnerPasswordTokenGranter granter;
private final RedisClientDetailsService redisClientDetailsService;
private final RemoteStaffService remoteStaffService;
private final RemoteStudentService remoteStudentService;
private final RemoteTeacherService remoteTeacherService;
private final RemoteSmartWelcomeService remoteSmartWelcomeService;
private final WxUtil wxUtil;
private final WxPublicUtil wxPublicUtil;
private final RedisService redisService;
private final CommonService commonService;
@Override
public AuthRequest renderAuth(String oauthType) throws FebsException {
return factory.get(getAuthSource(oauthType));
}
@Override
public FebsResponse resolveBind(String oauthType, AuthCallback callback) throws FebsException {
FebsResponse febsResponse = new FebsResponse();
AuthRequest authRequest = factory.get(getAuthSource(oauthType));
AuthResponse<?> response = authRequest.login(resolveAuthCallback(callback));
if (response.ok()) {
febsResponse.data(response.getData());
} else {
throw new FebsException(String.format("第三方登录失败,%s", response.getMsg()));
}
return febsResponse;
}
@Override
public FebsResponse resolveLogin(String oauthType, AuthCallback callback) throws FebsException {
FebsResponse febsResponse = new FebsResponse();
AuthRequest authRequest = factory.get(getAuthSource(oauthType));
AuthResponse<?> response = authRequest.login(resolveAuthCallback(callback));
if (response.ok()) {
AuthUser authUser = (AuthUser) response.getData();
UserConnection userConnection = userConnectionService
.selectByCondition(authUser.getSource().toString(), authUser.getUuid());
if (userConnection == null) {
febsResponse.message(NOT_BIND).data(authUser);
} else {
SystemUser user = userManager.findByName(userConnection.getUserName());
if (user == null) {
throw new FebsException("系统中未找到与第三方账号对应的账户");
}
OAuth2AccessToken oAuth2AccessToken = getOauth2AccessToken(user);
febsResponse.message(SOCIAL_LOGIN_SUCCESS).data(oAuth2AccessToken);
febsResponse.put(USERNAME, user.getUsername());
}
} else {
throw new FebsException(String.format("第三方登录失败,%s", response.getMsg()));
}
return febsResponse;
}
@Override
public OAuth2AccessToken bindLogin(BindUser bindUser, AuthUser authUser) throws FebsException {
SystemUser systemUser = userManager.findByName(bindUser.getBindUsername());
if (systemUser == null || !passwordEncoder.matches(bindUser.getBindPassword(), systemUser.getPassword())) {
throw new FebsException("绑定系统账号失败,用户名或密码错误!");
}
this.createConnection(systemUser, authUser);
return this.getOauth2AccessToken(systemUser);
}
@Override
public OAuth2AccessToken signLogin(BindUser registUser, AuthUser authUser) throws FebsException {
SystemUser user = this.userManager.findByName(registUser.getBindUsername());
if (user != null) {
throw new FebsException("该用户名已存在!");
}
String encryptPassword = passwordEncoder.encode(registUser.getBindPassword());
SystemUser systemUser = this.userManager.registUser(registUser.getBindUsername(), encryptPassword);
this.createConnection(systemUser, authUser);
return this.getOauth2AccessToken(systemUser);
}
@Override
public void bind(BindUser bindUser, AuthUser authUser) throws FebsException {
String username = bindUser.getBindUsername();
if (isCurrentUser(username)) {
UserConnection userConnection = userConnectionService
.selectByCondition(authUser.getSource().toString(), authUser.getUuid());
if (userConnection != null) {
throw new FebsException("绑定失败,该第三方账号已绑定" + userConnection.getUserName() + "系统账户");
}
SystemUser systemUser = new SystemUser();
systemUser.setUsername(username);
this.createConnection(systemUser, authUser);
} else {
throw new FebsException("绑定失败,您无权绑定别人的账号");
}
}
@Override
public void unbind(BindUser bindUser, String oauthType) throws FebsException {
String username = bindUser.getBindUsername();
if (isCurrentUser(username)) {
this.userConnectionService.deleteByCondition(username, oauthType);
} else {
throw new FebsException("解绑失败,您无权解绑别人的账号");
}
}
@Override
public List<UserConnection> findUserConnections(String username) {
return this.userConnectionService.selectByCondition(username);
}
@Override
public Map<String, Object> qywxLogin(String code, String state, String corpId, Integer type) {
log.info("code:{},state:{},corpid:{}", code, state, corpId);
Dept school = commonService.getSchoolByCorpId(corpId);
// 获取secret
EduApp eduApp = commonService.getAppByCodeAndSchool(state, corpId, null);
log.info("corpId:{},app:{}", corpId, eduApp != null ? eduApp.toString() : "null");
if (eduApp == null || StrUtil.isBlank(eduApp.getWxSecret())) {
throw new FebsException("该应用暂未登记");
}
// 获取access token
String accessToken = wxUtil.getAccessToken(corpId, eduApp.getWxSecret());
// 查询用户信息
JSONObject user = wxUtil.getUserInfo(accessToken, code);
log.info("user:[{}]", user);
// 分别为 企业用户 家长
ParentInfoVO parent = null;
EduStaff teacher = null;
// 家长和教师标识
Object userId = user.get("UserId");
Object parentUserid = user.get("parent_userid");
if (type == 0) {
if (userId != null) {
teacher = remoteStaffService
.getStaffByWxOrMobileOrOpenId(userId.toString(), null, null, school.getDeptId())
.getData();
} else if (parentUserid != null) {
// 通过家长信息转化为职工信息
parent = remoteStudentService.getParentWithStudent(null, parentUserid.toString(),
school.getDeptId()).getData();
if (parent != null) {
teacher = remoteStaffService
.getStaffByWxOrMobileOrOpenId(null, parent.getMobile(), null, school.getDeptId())
.getData();
}
}
} else {
if (parentUserid != null) {
parent = remoteStudentService.getParentWithStudent(null, parentUserid.toString(),
school.getDeptId()).getData();
} else if (userId != null) {
// 通过职工信息转化为家长信息
teacher = remoteStaffService
.getStaffByWxOrMobileOrOpenId(userId.toString(), null, null, school.getDeptId())
.getData();
if (teacher != null) {
parent = remoteStudentService.getParentWithStudent(teacher.getMobile(), null,
school.getDeptId()).getData();
}
}
}
if (teacher == null && parent == null) {
throw new FebsException("该用户暂未注册");
}
// 教师用户查询是否绑定手机号(家长必定有手机号)
if (type == 0 && StrUtil.isBlank(teacher.getMobile())) {
throw new FebsException("请绑定企业微信手机号");
}
redisService.hset(CachePrefixConstant.USER_LOGIN_DEPT, type == 0 ? teacher.getMobile() :
parent.getMobile(), school.getDeptId());
// 查询系统用户
SystemUser systemUser = userManager.findByName(type == 0 ? teacher.getMobile() : parent.getMobile());
// 系统用户不存在 进行注册
if (systemUser == null) {
systemUser = new SystemUser();
systemUser.setNickname(type == 0 ? teacher.getName() : parent.getMobile());
systemUser.setUsername(type == 0 ? teacher.getMobile() : parent.getMobile());
systemUser.setPassword(passwordEncoder.encode(SystemUser.DEFAULT_PASSWORD));
systemUser.setSex(type == 0 ? teacher.getSex() : "2");
systemUser.setAvatar(type == 0 ? teacher.getAvatar() : SystemUser.DEFAULT_AVATAR);
systemUser.setMobile(type == 0 ? teacher.getMobile() : parent.getMobile());
systemUser.setUserOrigin(0);
systemUser.setDeptId(school.getDeptId());
systemUser.setRoleName(type == 0 ? RoleName.STAFF.getName() : RoleName.PARENT.getName());
systemUser.setUserType(type == 0 ? 1 : 0);
// systemUser = remoteUserService.addUser(systemUser).getData();
userManager.registUser(systemUser);
} else if (!systemUser.getRoleName().contains(type == 0 ? RoleName.STAFF.getName() : RoleName.PARENT.getName())) {
Role role = roleMapper.selectOne(Wrappers.<Role>lambdaQuery()
.eq(Role::getRoleName, type == 0 ? RoleName.STAFF.getName() : RoleName.PARENT.getName()));
userManager.addRole(systemUser.getUserId(), role.getRoleId());
systemUser.setRoleId(systemUser.getRoleId() + "," + role.getRoleId());
systemUser.setRoleName(systemUser.getRoleName() + "," + role.getRoleName());
}
// 登录返回token和用户信息
Map<String, Object> res = new HashMap<>();
res.put("token", getOauth2AccessToken(systemUser));
systemUser.setPassword(null);
systemUser.setIdentityId(type == 0 ? teacher.getId() : parent.getParentId());
res.put("user", systemUser);
res.put("identity", type == 0 ? teacher : parent);
return res;
}
@Override
public Map<String, Object> appLogin(String areaId, Long schoolId, String mobile, String userId,
Integer type) {
Dept school = commonService.getDept(schoolId);
Asserts.isNotNull(school, "该学校不存在");
// 分别为 企业用户 家长
ParentInfoVO parent = null;
EduStaff teacher = null;
// 家长和教师标识
if (type == 0) {
teacher = remoteStaffService.getStaffByWxOrMobileOrOpenId(null, mobile, null, school.getDeptId()).getData();
} else {
parent = remoteStudentService.getParentWithStudent(mobile, null, school.getDeptId()).getData();
}
if (teacher == null && parent == null) {
throw new FebsException("该用户未注册");
}
redisService.hset(CachePrefixConstant.USER_LOGIN_DEPT, type == 0 ? teacher.getMobile() :
parent.getMobile(), school.getDeptId());
// 查询系统用户
SystemUser systemUser = userManager.findByName(type == 0 ? teacher.getMobile() : parent.getMobile());
// 系统用户不存在 进行注册
if (systemUser == null) {
// Role role = remoteRoleService.getRoleByNameNoPermission(type == 0 ? "教师" : "家长").getData();
systemUser = new SystemUser();
systemUser.setNickname(type == 0 ? teacher.getName() : parent.getMobile());
systemUser.setUsername(type == 0 ? teacher.getMobile() : parent.getMobile());
systemUser.setPassword(passwordEncoder.encode(SystemUser.DEFAULT_PASSWORD));
systemUser.setSex(type == 0 ? teacher.getSex() : "2");
systemUser.setAvatar(type == 0 ? teacher.getAvatar() : SystemUser.DEFAULT_AVATAR);
systemUser.setMobile(type == 0 ? teacher.getMobile() : parent.getMobile());
systemUser.setUserOrigin(0);
systemUser.setDeptId(type == 0 ? teacher.getSchoolId() : schoolId);
systemUser.setRoleName(type == 0 ? RoleName.STAFF.getName() : RoleName.PARENT.getName());
systemUser.setUserType(type == 0 ? 1 : 0);
systemUser.setYidaAppUserId(userId);
userManager.registUser(systemUser);
} else if (!systemUser.getRoleName().contains(type == 0 ? RoleName.STAFF.getName() : RoleName.PARENT.getName())) {
Role role = roleMapper.selectOne(Wrappers.<Role>lambdaQuery()
.eq(Role::getRoleName, type == 0 ? RoleName.STAFF.getName() : RoleName.PARENT.getName()));
userManager.addRole(systemUser.getUserId(), role.getRoleId());
systemUser.setRoleId(systemUser.getRoleId() + "," + role.getRoleId());
systemUser.setRoleName(systemUser.getRoleName() + "," + role.getRoleName());
}
// 用户未绑定appid
if (type == 0 && teacher.getYidaAppUserId() == null) {
remoteTeacherService.saveYidaAppUserId(teacher.getId(), userId);
} else if (type == 1 && parent.getParentYidaAppId() == null) {
remoteStudentService.saveYidaAppUserId(parent.getParentId(), userId);
}
// 登录返回token和用户信息
Map<String, Object> res = new HashMap<>();
res.put("token", getOauth2AccessToken(systemUser));
systemUser.setPassword(null);
systemUser.setIdentityId(type == 0 ? teacher.getId() : parent.getParentId());
res.put("user", systemUser);
res.put("identity", type == 0 ? teacher : parent);
return res;
}
// @Override
// public Map<String, Object> wxpublicLogin(String code, String appId, Integer type, Long schoolId) {
// Dept school = null;
// if (StringUtils.isNotBlank(appId)) {
// school = commonService.getSchoolByAppId(appId);
// } else if (Objects.nonNull(schoolId)) {
// school = commonService.getDept(schoolId);
// }
// if (Objects.isNull(school)) {
// throw new FebsException("学校信息错误");
// }
//
// EduDeptWxPublic wxPublic = commonService.getWxPublic(school.getDeptId());
// // 查询用户信息
// String openId = wxPublicUtil.getOpenId(wxPublic.getAppId(), wxPublic.getSecret(), code);
// log.info(openId);
// // 分别为 企业用户 家长
// ParentInfoVO parent = null;
// EduStaff staff = null;
// if (type == 0) {
// staff = remoteStaffService.getStaffByWxOrMobileOrOpenId(null, null, openId, school.getDeptId()).getData();
// } else {
// }
// if (staff == null && parent == null) {
// throw new FebsException("该用户暂未注册");
// }
// // 教师用户查询是否绑定手机号(家长必定有手机号)
// if (type == 0 && StrUtil.isBlank(staff.getMobile())) {
// throw new FebsException("请绑定手机号");
// }
// redisService.hset(CachePrefixConstant.USER_LOGIN_DEPT, type == 0 ? staff.getMobile() :
// parent.getMobile(), school.getDeptId());
// // 查询系统用户
// SystemUser systemUser = userManager.findByName(type == 0 ? staff.getMobile() : parent.getMobile());
// // 系统用户不存在 进行注册
// if (systemUser == null) {
//// Role role = remoteRoleService.getRoleByNameNoPermission(type == 0 ? "教师" : "家长").getData();
// systemUser = new SystemUser();
// systemUser.setNickname(type == 0 ? staff.getName() : parent.getMobile());
// systemUser.setUsername(type == 0 ? staff.getMobile() : parent.getMobile());
// systemUser.setPassword(passwordEncoder.encode(SystemUser.DEFAULT_PASSWORD));
// systemUser.setSex(type == 0 ? staff.getSex() : "2");
// systemUser.setAvatar(type == 0 ? staff.getAvatar() : SystemUser.DEFAULT_AVATAR);
// systemUser.setMobile(type == 0 ? staff.getMobile() : parent.getMobile());
// systemUser.setUserOrigin(0);
// systemUser.setDeptId(school.getDeptId());
// systemUser.setRoleName(type == 0 ? RoleName.STAFF.getName() : RoleName.PARENT.getName());
// systemUser.setUserType(type == 0 ? 1 : 0);
// userManager.registUser(systemUser);
// } else if (!systemUser.getRoleName().contains(type == 0 ? RoleName.STAFF.getName() : RoleName.PARENT.getName())) {
// Role role = roleMapper.selectOne(Wrappers.<Role>lambdaQuery()
// .eq(Role::getRoleName, type == 0 ? RoleName.STAFF.getName() : RoleName.PARENT.getName()));
// userManager.addRole(systemUser.getUserId(), role.getRoleId());
// systemUser.setRoleId(systemUser.getRoleId() + "," + role.getRoleId());
// systemUser.setRoleName(systemUser.getRoleName() + "," + role.getRoleName());
// }
// // 登录返回token和用户信息
// Map<String, Object> res = new HashMap<>();
// res.put("token", getOauth2AccessToken(systemUser));
// systemUser.setPassword(null);
// systemUser.setIdentityId(type == 0 ? staff.getId() : parent.getParentId());
// res.put("user", systemUser);
// res.put("identity", type == 0 ? staff : parent);
// return res;
// }
@Override
public Map<String, Object> wxpublicLogin(String code, String appId, Integer type, Long schoolId, String mobile) {
Dept school = null;
if (StringUtils.isNotBlank(appId)) {
school = commonService.getSchoolByAppId(appId);
} else if (Objects.nonNull(schoolId)) {
school = commonService.getDept(schoolId);
}
if (Objects.isNull(school)) {
throw new FebsException("学校信息错误");
}
// 查询用户信息
String openId = wxPublicUtil.getOpenId(school.getWxPublicAppId(), school.getWxPublicSecret(), code);
// 分别为 企业用户 家长
ParentInfoVO parent = null;
EduStaff teacher = null;
// 家长和教师标识
if (type == 0) {
teacher = remoteStaffService.getStaffByWxOrMobileOrOpenId(null, mobile, null, school.getDeptId()).getData();
} else {
parent = remoteStudentService.getParentWithStudent(mobile, null, school.getDeptId()).getData();
}
if (teacher == null && parent == null) {
throw new FebsException("该用户暂未注册");
}
redisService.hset(CachePrefixConstant.USER_LOGIN_DEPT, type == 0 ? teacher.getMobile() :
parent.getMobile(), school.getDeptId());
if (type == 0) {
teacher.setWxPublicOpenId(openId);
remoteStaffService.saveWxPublicOpenId(teacher.getId(), openId);
} else if (type == 1) {
remoteStudentService.saveWxPublicOpenId(parent.getParentId(), openId);
}
// 查询系统用户
SystemUser systemUser = userManager.findByName(type == 0 ? teacher.getMobile() : parent.getMobile());
// 系统用户不存在 进行注册
if (systemUser == null) {
systemUser = new SystemUser();
systemUser.setNickname(type == 0 ? teacher.getName() : parent.getMobile());
systemUser.setUsername(type == 0 ? teacher.getMobile() : parent.getMobile());
systemUser.setPassword(passwordEncoder.encode(SystemUser.DEFAULT_PASSWORD));
systemUser.setSex(type == 0 ? teacher.getSex() : "2");
systemUser.setAvatar(type == 0 ? teacher.getAvatar() : SystemUser.DEFAULT_AVATAR);
systemUser.setMobile(type == 0 ? teacher.getMobile() : parent.getMobile());
systemUser.setUserOrigin(0);
systemUser.setDeptId(school.getDeptId());
systemUser.setRoleName(type == 0 ? RoleName.STAFF.getName() : RoleName.PARENT.getName());
systemUser.setUserType(type == 0 ? 1 : 0);
userManager.registUser(systemUser);
} else if (!systemUser.getRoleName().contains(type == 0 ? RoleName.STAFF.getName() : RoleName.PARENT.getName())) {
Role role = roleMapper.selectOne(Wrappers.<Role>lambdaQuery()
.eq(Role::getRoleName, type == 0 ? RoleName.STAFF.getName() : RoleName.PARENT.getName()));
userManager.addRole(systemUser.getUserId(), role.getRoleId());
systemUser.setRoleId(systemUser.getRoleId() + "," + role.getRoleId());
systemUser.setRoleName(systemUser.getRoleName() + "," + role.getRoleName());
}
// 登录返回token和用户信息
Map<String, Object> res = new HashMap<>();
res.put("token", getOauth2AccessToken(systemUser));
systemUser.setPassword(null);
systemUser.setIdentityId(type == 0 ? teacher.getId() : parent.getParentId());
res.put("user", systemUser);
res.put("identity", type == 0 ? teacher : parent);
return res;
}
private void createConnection(SystemUser systemUser, AuthUser authUser) {
UserConnection userConnection = new UserConnection();
userConnection.setUserName(systemUser.getUsername());
userConnection.setProviderName(authUser.getSource().toString());
userConnection.setProviderUserId(authUser.getUuid());
userConnection.setProviderUserName(authUser.getUsername());
userConnection.setImageUrl(authUser.getAvatar());
userConnection.setNickName(authUser.getNickname());
userConnection.setLocation(authUser.getLocation());
this.userConnectionService.createUserConnection(userConnection);
}
private AuthCallback resolveAuthCallback(AuthCallback callback) {
int stateLength = 3;
String state = callback.getState();
String[] strings = StringUtils.splitByWholeSeparatorPreserveAllTokens(state, StringConstant.DOUBLE_COLON);
if (strings.length == stateLength) {
callback.setState(strings[0] + StringConstant.DOUBLE_COLON + strings[1]);
}
return callback;
}
private AuthSource getAuthSource(String type) throws FebsException {
if (StrUtil.isNotBlank(type)) {
return AuthSource.valueOf(type.toUpperCase());
} else {
throw new FebsException(String.format("暂不支持%s第三方登录", type));
}
}
private boolean isCurrentUser(String username) {
String currentUsername = FebsUtil.getCurrentUsername();
return StringUtils.equalsIgnoreCase(username, currentUsername);
}
private OAuth2AccessToken getOauth2AccessToken(SystemUser user) throws FebsException {
final HttpServletRequest httpServletRequest = FebsUtil.getHttpServletRequest();
httpServletRequest.setAttribute(ParamsConstant.LOGIN_TYPE, SocialConstant.SOCIAL_LOGIN);
String socialLoginClientId = properties.getSocialLoginClientId();
ClientDetails clientDetails = null;
try {
clientDetails = redisClientDetailsService.loadClientByClientId(socialLoginClientId);
} catch (Exception e) {
throw new FebsException("获取第三方登录可用的Client失败");
}
if (clientDetails == null) {
throw new FebsException("未找到第三方登录可用的Client");
}
Map<String, String> requestParameters = new HashMap<>(5);
requestParameters.put(ParamsConstant.GRANT_TYPE, GrantTypeConstant.PASSWORD);
requestParameters.put(USERNAME, user.getUsername());
requestParameters.put(PASSWORD, SocialConstant.setSocialLoginPassword());
String grantTypes = String.join(StringConstant.COMMA, clientDetails.getAuthorizedGrantTypes());
TokenRequest tokenRequest = new TokenRequest(requestParameters, clientDetails.getClientId(),
clientDetails.getScope(), grantTypes);
return granter.grant(GrantTypeConstant.PASSWORD, tokenRequest);
}
@Override
public Map<String, Object> h5Login(Long schoolId, String mobile, Integer type) {
Dept school = commonService.getDept(schoolId);
Asserts.isNotNull(school, "该学校不存在");
// 分别为 企业用户 家长
ParentInfoVO parent = null;
EduStaff teacher = null;
// 家长和教师标识
if (type == 0) {
teacher = remoteStaffService.getStaffByWxOrMobileOrOpenId(null, mobile, null, school.getDeptId()).getData();
} else {
parent = remoteStudentService.getParentWithStudent(mobile, null, school.getDeptId()).getData();
}
if (teacher == null && parent == null) {
throw new FebsException("该用户未注册");
}
redisService.hset(CachePrefixConstant.USER_LOGIN_DEPT, type == 0 ? teacher.getMobile() :
parent.getMobile(), school.getDeptId());
// 查询系统用户
SystemUser systemUser = userManager.findByName(type == 0 ? teacher.getMobile() : parent.getMobile());
// 系统用户不存在 进行注册
if (systemUser == null) {
systemUser = new SystemUser();
systemUser.setNickname(type == 0 ? teacher.getName() : parent.getMobile());
systemUser.setUsername(type == 0 ? teacher.getMobile() : parent.getMobile());
systemUser.setPassword(passwordEncoder.encode(SystemUser.DEFAULT_PASSWORD));
systemUser.setSex(type == 0 ? teacher.getSex() : "2");
systemUser.setAvatar(type == 0 ? teacher.getAvatar() : SystemUser.DEFAULT_AVATAR);
systemUser.setMobile(type == 0 ? teacher.getMobile() : parent.getMobile());
systemUser.setUserOrigin(0);
systemUser.setDeptId(type == 0 ? teacher.getSchoolId() : schoolId);
systemUser.setRoleName(type == 0 ? RoleName.STAFF.getName() : RoleName.PARENT.getName());
systemUser.setUserType(type == 0 ? 1 : 0);
userManager.registUser(systemUser);
} else if (!systemUser.getRoleName().contains(type == 0 ? RoleName.STAFF.getName() : RoleName.PARENT.getName())) {
Role role = roleMapper.selectOne(Wrappers.<Role>lambdaQuery()
.eq(Role::getRoleName, type == 0 ? RoleName.STAFF.getName() : RoleName.PARENT.getName()));
userManager.addRole(systemUser.getUserId(), role.getRoleId());
systemUser.setRoleId(systemUser.getRoleId() + "," + role.getRoleId());
systemUser.setRoleName(systemUser.getRoleName() + "," + role.getRoleName());
}
// 登录返回token和用户信息
Map<String, Object> res = new HashMap<>();
res.put("token", getOauth2AccessToken(systemUser));
systemUser.setPassword(null);
systemUser.setIdentityId(type == 0 ? teacher.getId() : parent.getParentId());
res.put("user", systemUser);
res.put("identity", type == 0 ? teacher : parent);
return res;
}
@Override
public Map<String, Object> checkOpenId(String code, Long deptId) {
Dept school = commonService.getDept(deptId);
if (Objects.isNull(school)) {
throw new FebsException("学校信息错误");
}
// 查询用户信息
String openId = wxPublicUtil.getOpenId(school.getWxPublicAppId(), school.getWxPublicSecret(), code);
// 查询openId是否绑定了对应的信息
EduSmartWelcomeGuideRosterRelation relation = remoteSmartWelcomeService.getOpenIdRelation(openId, deptId).getData();
Map<String, Object> map = new HashMap<>();
// true代表已经绑定,false代表未绑定
map.put("status", Objects.nonNull(relation));
// 微信openId
map.put("openId", openId);
return map;
}
@Override
public Map<String, Object> wxPublicLogin(String openId, Long deptId) {
// 查询关联关系
EduSmartWelcomeGuideRosterRelation relation = remoteSmartWelcomeService.getOpenIdRelation(openId, deptId).getData();
if (Objects.isNull(relation) || Objects.isNull(relation.getGuideRoster())) {
throw new FebsException("信息错误");
}
// 用户信息
SystemUser systemUser = userManager.findByName(relation.getGuideRoster().getMobile());
// 登录返回token和用户信息
Map<String, Object> res = new HashMap<>();
res.put("token", getOauth2AccessToken(systemUser));
systemUser.setPassword(null);
res.put("user", systemUser);
// // 查询人员步骤信息
// List<EduSmartWelcomeGuideStep> stepList = remoteSmartWelcomeService.listStepInfo(relation.getId(), relation.getGuideId()).getData();
// relation.setStepList(stepList);
res.put("info", relation);
return res;
}
}
@@ -0,0 +1,53 @@
package com.yida.data.auth.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import com.yida.data.auth.mapper.UserConnectionMapper;
import com.yida.data.auth.service.UserConnectionService;
import com.yida.data.common.core.entity.system.UserConnection;
/**
* @author MrBird
*/
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class UserConnectionServiceImpl extends ServiceImpl<UserConnectionMapper, UserConnection> implements UserConnectionService {
@Override
public UserConnection selectByCondition(String providerName, String providerUserId) {
LambdaQueryWrapper<UserConnection> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserConnection::getProviderName, providerName)
.eq(UserConnection::getProviderUserId, providerUserId);
return this.baseMapper.selectOne(queryWrapper);
}
@Override
public List<UserConnection> selectByCondition(String username) {
LambdaQueryWrapper<UserConnection> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserConnection::getUserName, username);
return this.baseMapper.selectList(queryWrapper);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void createUserConnection(UserConnection userConnection) {
this.baseMapper.insert(userConnection);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteByCondition(String username, String providerName) {
LambdaQueryWrapper<UserConnection> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserConnection::getUserName, username);
queryWrapper.eq(UserConnection::getProviderName, providerName);
this.remove(queryWrapper);
}
}
@@ -0,0 +1,85 @@
package com.yida.data.auth.service.impl;
import com.yida.data.auth.properties.FebsAuthProperties;
import com.yida.data.auth.properties.FebsValidateCodeProperties;
import com.yida.data.auth.service.ValidateCodeService;
import com.yida.data.common.core.entity.constant.FebsConstant;
import com.yida.data.common.core.entity.constant.ImageTypeConstant;
import com.yida.data.common.core.entity.constant.ParamsConstant;
import com.yida.data.common.core.exception.ValidateCodeException;
import cc.mrbird.febs.common.redis.service.RedisService;
import com.wf.captcha.GifCaptcha;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 验证码服务
*
* @author MrBird
*/
@Service
@RequiredArgsConstructor
public class ValidateCodeServiceImpl implements ValidateCodeService {
private final RedisService redisService;
private final FebsAuthProperties properties;
@Override
public void create(HttpServletRequest request, HttpServletResponse response) throws IOException, ValidateCodeException {
String key = request.getParameter(ParamsConstant.VALIDATE_CODE_KEY);
if (StringUtils.isBlank(key)) {
throw new ValidateCodeException("验证码key不能为空");
}
FebsValidateCodeProperties code = properties.getCode();
setHeader(response, code.getType());
Captcha captcha = createCaptcha(code);
redisService.set(FebsConstant.CODE_PREFIX + key, StringUtils.lowerCase(captcha.text()), code.getTime());
captcha.out(response.getOutputStream());
}
@Override
public void check(String key, String value) throws ValidateCodeException {
Object codeInRedis = redisService.get(FebsConstant.CODE_PREFIX + key);
if (StringUtils.isBlank(value)) {
throw new ValidateCodeException("请输入验证码");
}
if (codeInRedis == null) {
throw new ValidateCodeException("验证码已过期");
}
if (!StringUtils.equalsIgnoreCase(value, String.valueOf(codeInRedis))) {
throw new ValidateCodeException("验证码不正确");
}
}
private Captcha createCaptcha(FebsValidateCodeProperties code) {
Captcha captcha = null;
if (StringUtils.equalsIgnoreCase(code.getType(), ImageTypeConstant.GIF)) {
captcha = new GifCaptcha(code.getWidth(), code.getHeight(), code.getLength());
} else {
captcha = new SpecCaptcha(code.getWidth(), code.getHeight(), code.getLength());
}
captcha.setCharType(code.getCharType());
return captcha;
}
private void setHeader(HttpServletResponse response, String type) {
if (StringUtils.equalsIgnoreCase(type, ImageTypeConstant.GIF)) {
response.setContentType(MediaType.IMAGE_GIF_VALUE);
} else {
response.setContentType(MediaType.IMAGE_PNG_VALUE);
}
response.setHeader(HttpHeaders.PRAGMA, "No-cache");
response.setHeader(HttpHeaders.CACHE_CONTROL, "No-cache");
response.setDateHeader(HttpHeaders.EXPIRES, 0L);
}
}
@@ -0,0 +1,82 @@
package com.yida.data.auth.translator;
import com.yida.data.common.core.entity.FebsResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.exceptions.BadClientCredentialsException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.common.exceptions.RedirectMismatchException;
import org.springframework.security.oauth2.common.exceptions.UnsupportedGrantTypeException;
import org.springframework.security.oauth2.common.exceptions.UnsupportedResponseTypeException;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
/**
* 异常翻译
*
* @author MrBird
*/
@Slf4j
@Component
@SuppressWarnings("all")
public class FebsWebResponseExceptionTranslator implements WebResponseExceptionTranslator {
@Override
public ResponseEntity<?> translate(Exception e) {
ResponseEntity.BodyBuilder status = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
ResponseEntity.BodyBuilder status1 = ResponseEntity.status(HttpStatus.PAYMENT_REQUIRED);
FebsResponse response = new FebsResponse();
String message = "认证失败";
log.error(message, e);
if (e instanceof UnsupportedGrantTypeException) {
message = "不支持该认证类型";
return status.body(response.message(message));
}
if (e instanceof InvalidTokenException
&& StringUtils.containsIgnoreCase(e.getMessage(), "Invalid refresh token (expired)")) {
message = "刷新令牌已过期,请重新登录";
return status.body(response.message(message));
}
if (e instanceof InvalidScopeException) {
message = "不是有效的scope值";
return status.body(response.message(message));
}
if (e instanceof RedirectMismatchException) {
message = "redirect_uri值不正确";
return status.body(response.message(message));
}
if (e instanceof BadClientCredentialsException) {
message = "client值不合法";
return status.body(response.message(message));
}
if (e instanceof UnsupportedResponseTypeException) {
String code = StringUtils.substringBetween(e.getMessage(), "[", "]");
message = code + "不是合法的response_type值";
return status.body(response.message(message));
}
if (e instanceof InvalidGrantException) {
if (StringUtils.containsIgnoreCase(e.getMessage(), "Invalid refresh token")) {
message = "refresh token无效";
return status1.body(response.message(message));
}
if (StringUtils.containsIgnoreCase(e.getMessage(), "Invalid authorization code")) {
String code = StringUtils.substringAfterLast(e.getMessage(), ": ");
message = "授权码" + code + "不合法";
return status.body(response.message(message));
}
if (StringUtils.containsIgnoreCase(e.getMessage(), "locked")) {
message = "用户已被锁定,请联系管理员";
return status.body(response.message(message));
}
message = "用户名或密码错误";
return status.body(response.message(message));
}
return status.body(response.message(message));
}
}
@@ -0,0 +1,59 @@
package com.yida.data.auth.util;
import com.yida.data.common.core.entity.constant.GrantTypeConstant;
import com.yida.data.common.core.entity.constant.ParamsConstant;
import com.yida.data.common.core.entity.constant.SocialConstant;
import com.yida.data.common.core.entity.constant.StringConstant;
import com.yida.data.common.core.entity.system.SystemUser;
import com.yida.data.common.core.exception.FebsException;
import com.yida.data.common.core.utils.FebsUtil;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import com.yida.data.auth.properties.FebsAuthProperties;
import com.yida.data.auth.service.impl.RedisClientDetailsService;
import lombok.RequiredArgsConstructor;
@Component
@RequiredArgsConstructor
public class SecurityUtil {
private final FebsAuthProperties properties;
private final RedisClientDetailsService redisClientDetailsService;
private final ResourceOwnerPasswordTokenGranter granter;
private static final String USERNAME = "username";
private static final String PASSWORD = "password";
public OAuth2AccessToken getOauth2AccessToken(SystemUser user) throws FebsException {
final HttpServletRequest httpServletRequest = FebsUtil.getHttpServletRequest();
httpServletRequest.setAttribute(ParamsConstant.LOGIN_TYPE, SocialConstant.SOCIAL_LOGIN);
String socialLoginClientId = properties.getSocialLoginClientId();
ClientDetails clientDetails = null;
try {
clientDetails = redisClientDetailsService.loadClientByClientId(socialLoginClientId);
} catch (Exception e) {
throw new FebsException("获取第三方登录可用的Client失败");
}
if (clientDetails == null) {
throw new FebsException("未找到第三方登录可用的Client");
}
Map<String, String> requestParameters = new HashMap<>(5);
requestParameters.put(ParamsConstant.GRANT_TYPE, GrantTypeConstant.PASSWORD);
requestParameters.put(USERNAME, user.getUsername());
requestParameters.put(PASSWORD, SocialConstant.setSocialLoginPassword());
String grantTypes = String.join(StringConstant.COMMA, clientDetails.getAuthorizedGrantTypes());
TokenRequest tokenRequest = new TokenRequest(requestParameters, clientDetails.getClientId(), clientDetails.getScope(), grantTypes);
return granter.grant(GrantTypeConstant.PASSWORD, tokenRequest);
}
}
@@ -0,0 +1,3 @@
required=\u4E0D\u80FD\u4E3A\u7A7A
noMoreThan=\u957f\u5ea6\u4e0d\u80fd\u8d85\u8fc7{max}\u4e2a\u5b57\u7b26
invalidNumber=\u4e0d\u662f\u6709\u6548\u7684\u6570\u503c
@@ -0,0 +1,8 @@
|------------------------------|
| ____ ____ ___ __ |
| | |_ | |_ | |_) ( (` |
| |_| |_|__ |_|_) _)_) |
| |
| ${spring.application.name} |
| Spring-Boot: ${spring-boot.version} |
|------------------------------|
@@ -0,0 +1,34 @@
spring:
profiles:
active: "@env-name@"
application:
name: Edu-Auth
cloud:
nacos:
config:
server-addr: ${nacos.url}
group: DEFAULT_GROUP
prefix: edu-auth
file-extension: yaml
discovery:
server-addr: ${nacos.url}
thymeleaf:
cache: false
logging:
level:
org:
springframework:
boot:
actuate:
endpoint:
EndpointId: error
com:
alibaba:
cloud:
nacos:
client:
NacosPropertySourceBuilder: error
nacos:
client:
naming: warn
@@ -0,0 +1,9 @@
febs.auth.enableJwt=false
febs.auth.jwtAccessKey=febs
febs.auth.socialLoginClientId=app
febs.auth.code.time=120
febs.auth.code.type=png
febs.auth.code.width=115
febs.auth.code.height=42
febs.auth.code.length=4
febs.auth.code.charType=2
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yida.data.auth.mapper.MenuMapper">
<select id="findUserPermissions" resultType="java.lang.String">
select distinct m.perms
from t_role r
left join t_user_role ur on (r.role_id = ur.role_id)
left join t_user u on (u.user_id = ur.user_id)
left join t_role_menu rm on (rm.role_id = r.role_id)
left join t_menu m on (m.menu_id = rm.menu_id)
where u.username = #{userName}
and m.perms is not null
and m.perms &lt;&gt; ''
</select>
</mapper>
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yida.data.auth.mapper.UserMapper">
<select id="findByName" parameterType="string"
resultType="com.yida.data.common.core.entity.system.SystemUser">
SELECT u.user_id userId,
u.username,
u.nickname,
u.email,
u.mobile,
u.password,
u.status,
u.create_time createTime,
u.ssex sex,
u.dept_id deptId,
u.last_login_time lastLoginTime,
u.modify_time modifyTime,
u.description,
u.avatar,
d.dept_type deptType,
d.school_nature schoolNature,
d.dept_name deptName,
u.agent_id agentId,
GROUP_CONCAT(r.role_id) roleId,
GROUP_CONCAT(r.ROLE_NAME) roleName,
GROUP_CONCAT( r.ROLE_PERMS ) rolePerms,
u.create_id,
u.create_dept_id,
u.main_dept_id,
u.open_id
FROM t_user u
LEFT JOIN t_dept d ON (u.dept_id = d.dept_id)
LEFT JOIN t_user_role ur ON (u.user_id = ur.user_id)
LEFT JOIN t_role r ON r.role_id = ur.role_id
WHERE u.username = #{username}
group by u.username, u.user_id, u.email, u.mobile, u.password, u.status, u.create_time, u.ssex,
u.dept_id
, u.last_login_time, u.modify_time, u.description, u.avatar
</select>
<select id="findUserDataPermissions" parameterType="long"
resultType="com.yida.data.common.core.entity.system.UserDataPermission">
select user_id userId, dept_id deptId
from t_user_data_permission
where user_id = #{userId}
</select>
<select id="listUserByPerms" resultType="com.yida.data.common.core.entity.system.SystemUser">
select a.*
from t_user a left join t_user_role b on a.USER_ID=b.USER_ID
left join t_role c on b.ROLE_ID=c.ROLE_ID
where c.ROLE_PERMS=#{perms}
<if test="deptId != null">
and a.DEPT_ID=#{deptId}
</if>
order by a.USER_ID
</select>
<!-- 查询主角色code字符串 -->
<select id="findMainRole"
resultType="java.lang.String">
SELECT
distinct
r.ROLE_PERMS
FROM
t_user u
INNER JOIN t_user_role ur ON u.USER_ID = ur.USER_ID
INNER JOIN t_role r ON ur.ROLE_ID = r.ROLE_ID
WHERE
u.DEPT_ID = #{mainDeptId}
</select>
</mapper>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yida.data.auth.mapper.UserMenuMapper">
<select id="findUserPermissions" resultType="com.yida.data.common.core.entity.system.SystemUserMenu">
select um.*, m.perms
from t_user_menu um
left join t_user u on (u.user_id = um.user_id)
left join t_menu m on (m.menu_id = um.menu_id)
where u.username = #{userName}
and m.perms is not null
and m.perms <![CDATA[!=]]> ''
</select>
</mapper>
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
*{box-sizing:border-box;margin:0;padding:0;font-weight:600;}body{font-family:"Helvetica Neue",Helvetica,Tahoma,Arial,'PingFang SC','Source Han Sans CN','Source Han Sans','Hiragino Sans GB','WenQuanYi Micro Hei',sans-serif;color:white;font-weight:600;}body::-webkit-input-placeholder{font-family:"Helvetica Neue",Helvetica,Tahoma,Arial,'PingFang SC','Source Han Sans CN','Source Han Sans','Hiragino Sans GB','WenQuanYi Micro Hei',sans-serif;color:white;font-weight:600;}body:-moz-placeholder{font-family:"Helvetica Neue",Helvetica,Tahoma,Arial,'PingFang SC','Source Han Sans CN','Source Han Sans','Hiragino Sans GB','WenQuanYi Micro Hei',sans-serif;color:white;opacity:1;font-weight:600;}body::-moz-placeholder{font-family:"Helvetica Neue",Helvetica,Tahoma,Arial,'PingFang SC','Source Han Sans CN','Source Han Sans','Hiragino Sans GB','WenQuanYi Micro Hei',sans-serif;color:white;opacity:1;font-weight:600;}body:-ms-input-placeholder{font-family:"Helvetica Neue",Helvetica,Tahoma,Arial,'PingFang SC','Source Han Sans CN','Source Han Sans','Hiragino Sans GB','WenQuanYi Micro Hei',sans-serif;color:white;font-weight:600;}.wrapper{background-image:linear-gradient(120deg,#e0c3fc 0%,#8ec5fc 100%);position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;}.wrapper.form-success .container h1{-webkit-transform:translateY(85px);transform:translateY(85px);}.container{max-width:600px;margin:0 auto;padding:200px 0;height:400px;text-align:center;}.container h1{font-size:30px;-webkit-transition-duration:1s;transition-duration:1s;-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;font-weight:500;}form{padding:20px 0;position:relative;z-index:2;}form input{-webkit-appearance:none;-moz-appearance:none;appearance:none;outline:0;border:1px solid rgba(255,255,255,0.4);background-color:rgba(255,255,255,0.2);width:300px;border-radius:3px;padding:10px 15px;margin:0 auto 15px auto;display:block;text-align:center;font-size:18px;color:white;-webkit-transition-duration:0.25s;transition-duration:0.25s;font-weight:600;}form input:hover{background-color:rgba(255,255,255,0.4);}form input:focus{background-color:white;width:350px;color:#a6b1e1;}form button{-webkit-appearance:none;-moz-appearance:none;appearance:none;outline:0;background-color:white;border:0;padding:10px 15px;color:#a6b1e1;border-radius:3px;width:300px;cursor:pointer;font-size:18px;-webkit-transition-duration:0.25s;transition-duration:0.25s;}form button:hover{background-color:#f5f7f9;}.bg-bubbles{position:absolute;top:0;left:0;width:100%;height:100%;z-index:1;}.bg-bubbles li{position:absolute;list-style:none;display:block;width:40px;height:40px;background-color:rgba(255,255,255,0.15);bottom:-160px;-webkit-animation:square 25s infinite;animation:square 25s infinite;-webkit-transition-timing-function:linear;transition-timing-function:linear;}.bg-bubbles li:nth-child(1){left:10%;}.bg-bubbles li:nth-child(2){left:20%;width:80px;height:80px;-webkit-animation-delay:2s;animation-delay:2s;-webkit-animation-duration:17s;animation-duration:17s;}.bg-bubbles li:nth-child(3){left:25%;-webkit-animation-delay:4s;animation-delay:4s;}.bg-bubbles li:nth-child(4){left:40%;width:60px;height:60px;-webkit-animation-duration:22s;animation-duration:22s;background-color:rgba(255,255,255,0.25);}.bg-bubbles li:nth-child(5){left:70%;}.bg-bubbles li:nth-child(6){left:80%;width:120px;height:120px;-webkit-animation-delay:3s;animation-delay:3s;background-color:rgba(255,255,255,0.2);}.bg-bubbles li:nth-child(7){left:32%;width:160px;height:160px;-webkit-animation-delay:7s;animation-delay:7s;}.bg-bubbles li:nth-child(8){left:55%;width:20px;height:20px;-webkit-animation-delay:15s;animation-delay:15s;-webkit-animation-duration:40s;animation-duration:40s;}.bg-bubbles li:nth-child(9){left:25%;width:10px;height:10px;-webkit-animation-delay:2s;animation-delay:2s;-webkit-animation-duration:40s;animation-duration:40s;background-color:rgba(255,255,255,0.3);}.bg-bubbles li:nth-child(10){left:90%;width:160px;height:160px;-webkit-animation-delay:11s;animation-delay:11s;}@-webkit-keyframes square{0%{-webkit-transform:translateY(0);transform:translateY(0);}100%{-webkit-transform:translateY(-700px) rotate(600deg);transform:translateY(-700px) rotate(600deg);}}@keyframes square{0%{-webkit-transform:translateY(0);transform:translateY(0);}100%{-webkit-transform:translateY(-700px) rotate(600deg);transform:translateY(-700px) rotate(600deg);}}
@@ -0,0 +1 @@
$(function(){var a=$("#username"),b=$("#password");$("#login").on("click",function(c){var d,e;return c.preventDefault(),d=a.val().trim(),e=b.val().trim(),""===d?(alert("用户名不能为空"),void 0):""===e?(alert("密码不能为空"),void 0):($.post(ctx+"login",{username:d,password:e},function(a){window.location.href=a.data}).error(function(a){console.error(a),alert(a.responseJSON.message)}),void 0)})});
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="ch" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
name="viewport">
<meta content="ie=edge" http-equiv="X-UA-Compatible">
<link rel="icon" th:href="@{resource/favicon.ico}" type="image/x-icon"/>
<title>第三方登录失败</title>
</head>
<style>
span{font-size:.9rem;font-weight:bold;color:#42b983}
</style>
<body>
<span>[[${error}]]</span>
</body>
</html>
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="ch" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
name="viewport">
<meta name="description" content="FEBS Cloud授权码模式登录页面">
<meta name="author" content="MrBird">
<link rel="stylesheet" th:href="@{resource/login.min.css}" media="all">
<script th:src="@{resource/jQuery-2.1.4.min.js}"></script>
<link rel="icon" th:href="@{resource/favicon.ico}" type="image/x-icon"/>
<title>FEBS系统登录</title>
</head>
<body>
<div class="wrapper">
<div class="container"><h1>FEBS 系统登录</h1>
<form class="form"><input type="text" placeholder="用户名" id="username"><input type="password" placeholder="密码"
id="password">
<button type="submit" id="login">登录</button>
</form>
</div>
<ul class="bg-bubbles">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
</body>
<script th:inline="javascript">
var ctx = [[@{/}]];
</script>
<script th:src="@{resource/login.min.js}"></script>
</html>
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="ch" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
name="viewport">
<meta content="ie=edge" http-equiv="X-UA-Compatible">
<link rel="icon" th:href="@{resource/favicon.ico}" type="image/x-icon"/>
<title>登录跳转中</title>
</head>
<body>
登录中..
<script th:inline="javascript">
var response = [[${response}]];
var frontUrl = [[${frontUrl}]];
window.onload = function () {
window.opener.postMessage(response, frontUrl);
window.close();
}
</script>
</body>
</html>
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<a
href="https://open.weixin.qq.com/connect/oauth2/authorize?appid=wwbbc43a5b210ddb23&redirect_uri=http%3A%2F%2Fzbz.yd-data.com%3A8301%2Fauth%2Ftest&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect">test</a>
</body>
</html>