统一认证
This commit is contained in:
parent
621cc8f0c6
commit
b921722c2a
@ -7,6 +7,7 @@ import com.zhgd.annotation.OperLog;
|
||||
import com.zhgd.jeecg.common.api.vo.Result;
|
||||
import com.zhgd.jeecg.common.constant.CommonConstant;
|
||||
import com.zhgd.mybatis.Aes;
|
||||
import com.zhgd.xmgl.handler.exception.CustomException;
|
||||
import com.zhgd.xmgl.modules.basicdata.dto.SystemUserAuthDto;
|
||||
import com.zhgd.xmgl.modules.basicdata.entity.Government;
|
||||
import com.zhgd.xmgl.modules.basicdata.entity.SystemUser;
|
||||
@ -20,6 +21,7 @@ import com.zhgd.xmgl.security.JwtTokenProvider;
|
||||
import com.zhgd.xmgl.security.SecurityUser;
|
||||
import com.zhgd.xmgl.security.SecurityUtil;
|
||||
import com.zhgd.xmgl.util.CommonUtil;
|
||||
import com.zhgd.xmgl.util.JwtUtils;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
@ -27,12 +29,20 @@ import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import springfox.documentation.annotations.ApiIgnore;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
@ -229,4 +239,26 @@ public class SystemUserAuthController {
|
||||
return RestResult.fail().message("用户暂未登录");
|
||||
}
|
||||
}
|
||||
|
||||
@OperLog(operModul = "用户身份认证", operType="登录", operDesc = "用户身份认证")
|
||||
@Operation(summary = "用户身份认证", description = "用户身份认证")
|
||||
@PostMapping("/ssoLogin")
|
||||
public void ssoLogin(@RequestParam Map<String, Object> map, HttpServletResponse response) throws IOException {
|
||||
String token = MapUtils.getString(map, "id_token");
|
||||
String targetUrl = MapUtils.getString(map, "target_url");
|
||||
if (StringUtils.isEmpty(token)) {
|
||||
throw new CustomException("id_token 参数不存在");
|
||||
}
|
||||
// 公钥
|
||||
RSAPublicKey publicKey;
|
||||
try {
|
||||
ClassPathResource res = new ClassPathResource("sign.cer");
|
||||
publicKey = (RSAPublicKey) JwtUtils.readPublicKey(new String(
|
||||
FileCopyUtils.copyToByteArray(res.getInputStream()), StandardCharsets.UTF_8));
|
||||
} catch (CertificateException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String username = JwtUtils.getUserNameFromToken(token, publicKey);
|
||||
response.sendRedirect("http://jxjzw.zhgdyun.com:6080/#/home");
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,12 +3,17 @@ package com.zhgd.xmgl.modules.basicdata.controller.admin;
|
||||
import cn.xuyanwu.spring.file.storage.FileInfo;
|
||||
import cn.xuyanwu.spring.file.storage.FileStorageService;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.zhgd.annotation.OperLog;
|
||||
import com.zhgd.jeecg.common.api.vo.Result;
|
||||
import com.zhgd.xmgl.modules.basicdata.api.camera.MonitorApi;
|
||||
import com.zhgd.xmgl.modules.basicdata.entity.VideoConfig;
|
||||
import com.zhgd.xmgl.modules.basicdata.service.IVideoConfigService;
|
||||
import com.zhgd.xmgl.modules.basicdata.entity.GovernmentConfig;
|
||||
import com.zhgd.xmgl.modules.basicdata.service.IGovernmentConfigService;
|
||||
import com.zhgd.xmgl.modules.basicdata.service.IGovernmentService;
|
||||
import com.zhgd.xmgl.security.SecurityUser;
|
||||
import com.zhgd.xmgl.security.SecurityUtil;
|
||||
import com.zhgd.xmgl.util.HttpUtil;
|
||||
import com.zhgd.xmgl.util.ParamEnum;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
@ -42,7 +47,10 @@ public class VideoOpController {
|
||||
private FileStorageService fileStorageService;
|
||||
|
||||
@Autowired
|
||||
private IVideoConfigService videoConfigService;
|
||||
private IGovernmentService governmentService;
|
||||
|
||||
@Autowired
|
||||
private IGovernmentConfigService governmentConfigService;
|
||||
|
||||
/**
|
||||
* 获取监控点预览取流
|
||||
@ -52,8 +60,13 @@ public class VideoOpController {
|
||||
@OperLog(operModul = "视频操作管理", operType = "查询", operDesc = "获取配置信息")
|
||||
@ApiOperation(value = " 获取配置信息", notes = "获取配置信息", httpMethod = "GET")
|
||||
@GetMapping(value = "/getConfig")
|
||||
public Result<VideoConfig> getConfig() {
|
||||
return Result.success(videoConfigService.list().get(0));
|
||||
public Result<Object> getConfig() {
|
||||
SecurityUser user = SecurityUtil.getUser();
|
||||
String governmentSn = governmentService.getGovByUser(user.getAccountType(), user.getSn()).getGovernmentSn();
|
||||
GovernmentConfig config = governmentConfigService.getOne(Wrappers.<GovernmentConfig>lambdaQuery()
|
||||
.eq(GovernmentConfig::getGovernmentSn, governmentSn)
|
||||
.eq(GovernmentConfig::getConfigKey, ParamEnum.GovernmentConfig.VIDEO));
|
||||
return Result.success(JSONObject.parseObject(config.getValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -194,6 +194,7 @@ public class EntMonitorDevController {
|
||||
if (StringUtils.isNotBlank(engineeringName)) {
|
||||
wrapper.eq(Engineering::getEngineeringName, engineeringName);
|
||||
}
|
||||
wrapper.eq(Engineering::getExamineState, 3);
|
||||
List<Engineering> engineeringList = engineeringService.list(wrapper);
|
||||
List<MonitorTreeDto> monitorTrees = monitorDevService.treeByEngineering(engineeringList);
|
||||
return Result.success(monitorTrees);
|
||||
|
||||
@ -77,7 +77,7 @@ public class GovAcceptInspectRecordController {
|
||||
public Result<IPage<AcceptInspectRecordDto>> queryPageList(@ApiIgnore @RequestBody Map<String, Object> map) {
|
||||
Page<AcceptInspectRecord> page = PageUtil.getPage(map);
|
||||
QueryWrapper<AcceptInspectRecord> wrapper = Wrappers.<AcceptInspectRecord>query();
|
||||
wrapper.lambda().apply("FIND_IN_SET({0}, inspect_user)", SecurityUtil.getUser().getUserId());
|
||||
wrapper.lambda().apply("FIND_IN_SET({0}, inspect_user) OR create_by = {0}", SecurityUtil.getUser().getUserId());
|
||||
IPage<AcceptInspectRecordDto> pageList = acceptInspectRecordService.pageList(page, wrapper);
|
||||
return Result.success(pageList);
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package com.zhgd.xmgl.modules.basicdata.controller.project;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
@ -12,7 +11,6 @@ import com.zhgd.jeecg.common.system.query.QueryGenerator;
|
||||
import com.zhgd.jeecg.common.util.PageUtil;
|
||||
import com.zhgd.xmgl.modules.basicdata.constant.ParamConstants;
|
||||
import com.zhgd.xmgl.modules.basicdata.dto.MonitorTreeDto;
|
||||
import com.zhgd.xmgl.modules.basicdata.entity.Engineering;
|
||||
import com.zhgd.xmgl.modules.wisdom.entity.MonitorDev;
|
||||
import com.zhgd.xmgl.modules.wisdom.service.IMonitorDevService;
|
||||
import com.zhgd.xmgl.security.SecurityUser;
|
||||
@ -202,7 +200,6 @@ public class MonitorDevController {
|
||||
@PostMapping(value = "/tree")
|
||||
public Result<List<MonitorTreeDto>> tree(@ApiIgnore @RequestBody Map<String, Object> map) {
|
||||
String monitorName = MapUtils.getString(map, "monitorName");
|
||||
LambdaQueryWrapper<Engineering> wrapper = Wrappers.<Engineering>lambdaQuery();
|
||||
List<MonitorTreeDto> monitorTrees = monitorDevService.treeByEngineering(monitorName);
|
||||
return Result.success(monitorTrees);
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.zhgd.xmgl.modules.basicdata.mapper.SystemUserMapper">
|
||||
<sql id="userInfo">
|
||||
u.user_id, u.account, u.create_time, u.real_name, u.user_tel, u.department, u.state, u.password, u.email, u.sex, u.job_name, u.remark
|
||||
u.user_id, u.account, u.create_time, u.real_name, u.user_tel, u.department, u.state, u.password, u.show_password, u.email, u.sex, u.job_name, u.remark
|
||||
</sql>
|
||||
|
||||
<select id="findByUsername" resultType="com.zhgd.xmgl.modules.basicdata.entity.SystemUser" parameterType="string">
|
||||
|
||||
@ -107,6 +107,7 @@ public class SystemUserServiceImpl extends ServiceImpl<SystemUserMapper, SystemU
|
||||
systemUser.setCreateTime(new Date());
|
||||
systemUser.setSn(user.getSn());
|
||||
systemUser.setShowPassword(systemUser.getPassword());
|
||||
systemUser.setPassword(Aes.encrypt(systemUser.getPassword()));
|
||||
systemUser.setIsManager(false);
|
||||
systemUser.setAccountType(user.getAccountType());
|
||||
systemUser.setCreateBy(user.getUserId());
|
||||
|
||||
@ -42,6 +42,8 @@ public class UniPushServiceImpl implements UniPushService {
|
||||
|
||||
@Override
|
||||
public ApiResult pushListByAlias(List<String> alias, String title, String content, String payload) {
|
||||
String regex = "<[^>]+>";
|
||||
content = content.replaceAll(regex, "");
|
||||
PushDTO<Audience> pushDTO = this.buildPushDTO(title, content, payload);
|
||||
log.info("alias----{},title-----{},content-----{}", alias, title, content);
|
||||
log.info("pushDTO-----" + JSON.toJSONString(pushDTO));
|
||||
@ -144,7 +146,6 @@ public class UniPushServiceImpl implements UniPushService {
|
||||
AndroidDTO androidDTO = new AndroidDTO();
|
||||
Ups ups = new Ups();
|
||||
ThirdNotification notification1 = new ThirdNotification();
|
||||
ups.setNotification(notification1);
|
||||
//安卓离线展示的标题
|
||||
notification1.setTitle(title);
|
||||
//安卓离线展示的内容
|
||||
@ -153,6 +154,7 @@ public class UniPushServiceImpl implements UniPushService {
|
||||
notification1.setIntent("intent:#Intent;action=android.intent.action.oppopush;launchFlags=0x14000000;component=com.weixiaobao/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title=" + title + ";S.content=" + content + ";S.payload=" + payload + ";end");
|
||||
notification1.setPayload(payload);
|
||||
notification1.setUrl(payload);
|
||||
ups.setNotification(notification1);
|
||||
//各厂商自有功能单项设置
|
||||
//ups.addOption("HW", "/message/android/notification/badge/class", "io.dcloud.PandoraEntry ");
|
||||
//ups.addOption("HW", "/message/android/notification/badge/add_num", 1);
|
||||
@ -177,7 +179,7 @@ public class UniPushServiceImpl implements UniPushService {
|
||||
transmission.put("title", title);
|
||||
transmission.put("content", content);
|
||||
transmission.put("payload", payload);
|
||||
pushMessage.setTransmission(JSON.toJSONString(transmission));
|
||||
pushMessage.setTransmission(transmission.toString());
|
||||
pushDTO.setPushMessage(pushMessage);
|
||||
return pushDTO;
|
||||
}
|
||||
|
||||
@ -33,7 +33,25 @@ public class JwtTokenFilter extends OncePerRequestFilter {
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
if(securityEnable) {
|
||||
/*if (request.getRequestURI().equals("/jwt/sso")) {
|
||||
String token = request.getParameter("id_token");
|
||||
String targetUrl = request.getParameter("target_url");
|
||||
if (ObjectUtils.isEmpty(token)) {
|
||||
throw new CustomException("id_token 参数不存在");
|
||||
}
|
||||
// 公钥
|
||||
RSAPublicKey publicKey;
|
||||
try {
|
||||
ClassPathResource res = new ClassPathResource("sign.cer");
|
||||
publicKey = (RSAPublicKey) JwtUtils.readPublicKey(new String(
|
||||
FileCopyUtils.copyToByteArray(res.getInputStream()), StandardCharsets.UTF_8));
|
||||
} catch (CertificateException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String username = JwtUtils.getUserNameFromToken(token, publicKey);
|
||||
Authentication auth = jwtTokenProvider.getAuth(username);
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
} else*/ if(securityEnable) {
|
||||
String token = jwtTokenProvider.resolveToken(request);
|
||||
//try {
|
||||
if (token != null && jwtTokenProvider.validateToken(token)) {
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
package com.zhgd.xmgl.security;
|
||||
|
||||
import com.zhgd.xmgl.sso.jwt.JwtAuthenticationConfigurer;
|
||||
import com.zhgd.xmgl.sso.jwt.JwtAuthenticationFailureHandler;
|
||||
import com.zhgd.xmgl.sso.jwt.JwtAuthenticationSuccessHandler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@ -87,11 +90,12 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
.antMatchers("/xmgl/baseMenu/getAll").permitAll()
|
||||
.antMatchers("/xmgl/systemUser/analysis").permitAll()
|
||||
.antMatchers("/project/workerAttendance/add").permitAll()
|
||||
.antMatchers("/jwt/sso").permitAll()
|
||||
.antMatchers(HttpMethod.OPTIONS, "/**").anonymous()
|
||||
.anyRequest().authenticated() // 剩下所有的验证都需要验证
|
||||
.and()
|
||||
// 禁用 Spring Security 自带的跨域处理
|
||||
.csrf().disable()
|
||||
.csrf().disable().apply(jwtAuthenticationConfigurer()).and()
|
||||
// 授权异常
|
||||
.exceptionHandling()
|
||||
.authenticationEntryPoint(unauthorizedHandler)
|
||||
@ -167,4 +171,19 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JwtAuthenticationConfigurer jwtAuthenticationConfigurer() {
|
||||
return new JwtAuthenticationConfigurer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler() {
|
||||
return new JwtAuthenticationSuccessHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JwtAuthenticationFailureHandler jwtAuthenticationFailureHandler() {
|
||||
return new JwtAuthenticationFailureHandler();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
package com.zhgd.xmgl.sso.jwt;
|
||||
|
||||
import com.zhgd.xmgl.security.MyUserDetailsImpl;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AccountStatusUserDetailsChecker;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author SanLi
|
||||
* Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2023/7/26 20:56
|
||||
*/
|
||||
public class JwtAuthenticationConfigurer extends
|
||||
AbstractAuthenticationFilterConfigurer<HttpSecurity, JwtAuthenticationConfigurer, JwtAuthenticationFilter> implements ApplicationContextAware {
|
||||
|
||||
private static ApplicationContext applicationContext;;
|
||||
/**
|
||||
* Creates a new instance with minimal defaults
|
||||
*/
|
||||
public JwtAuthenticationConfigurer() {
|
||||
JwtAuthenticationFilter authenticationFilter = new JwtAuthenticationFilter();
|
||||
setAuthenticationFilter(authenticationFilter);
|
||||
super.failureHandler(new JwtAuthenticationFailureHandler());
|
||||
super.successHandler(new JwtAuthenticationSuccessHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the {@link RequestMatcher} given a loginProcessingUrl
|
||||
*
|
||||
* @param loginProcessingUrl creates the {@link RequestMatcher} based upon the
|
||||
* loginProcessingUrl
|
||||
* @return the {@link RequestMatcher} to use based upon the loginProcessingUrl
|
||||
*/
|
||||
@Override
|
||||
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
|
||||
return new OrRequestMatcher(
|
||||
new AntPathRequestMatcher(loginProcessingUrl, HttpMethod.GET.name()),
|
||||
new AntPathRequestMatcher(loginProcessingUrl, HttpMethod.POST.name()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(HttpSecurity http) {
|
||||
MyUserDetailsImpl detailsService = applicationContext.getBean(MyUserDetailsImpl.class);
|
||||
JwtAuthenticationProvider jwtAuthenticationProvider = new JwtAuthenticationProvider(
|
||||
detailsService, new AccountStatusUserDetailsChecker());
|
||||
http.authenticationProvider(jwtAuthenticationProvider);
|
||||
http.addFilterBefore(getAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
JwtAuthenticationConfigurer.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public static <T> Map<String, T> getBeansOfType(Class<T> baseType){
|
||||
return applicationContext.getBeansOfType(baseType);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.zhgd.xmgl.sso.jwt;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author SanLi
|
||||
* Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2023/7/26 21:19
|
||||
*/
|
||||
public class JwtAuthenticationFailureHandler implements AuthenticationFailureHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFailureHandler.class);
|
||||
|
||||
/**
|
||||
* Called when an authentication attempt fails.
|
||||
*
|
||||
* @param request the request during which the authentication attempt occurred.
|
||||
* @param response the response.
|
||||
* @param exception the exception which was thrown to reject the authentication
|
||||
* request.
|
||||
*/
|
||||
@Override
|
||||
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException exception) throws IOException,
|
||||
ServletException {
|
||||
logger.error("JWT 单点登录失败", exception);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* eiam-protocol-jwt - Employee Identity and Access Management Program
|
||||
* Copyright © 2020-2022 TopIAM (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.zhgd.xmgl.sso.jwt;
|
||||
|
||||
import com.zhgd.xmgl.handler.exception.CustomException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author SanLi
|
||||
* Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2023/7/4 22:21
|
||||
*/
|
||||
public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public JwtAuthenticationFilter() {
|
||||
super("/jwt/sso");
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs actual authentication.
|
||||
* <p>
|
||||
* The implementation should do one of the following:
|
||||
* <ol>
|
||||
* <li>Return a populated authentication token for the authenticated user, indicating
|
||||
* successful authentication</li>
|
||||
* <li>Return null, indicating that the authentication process is still in progress.
|
||||
* Before returning, the implementation should perform any additional work required to
|
||||
* complete the process.</li>
|
||||
* <li>Throw an <tt>AuthenticationException</tt> if the authentication process
|
||||
* fails</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param request from which to extract parameters and perform the authentication
|
||||
* @param response the response, which may be needed if the implementation has to do a
|
||||
* redirect as part of a multi-stage authentication process (such as OIDC).
|
||||
* @return the authenticated user token, or null if authentication is incomplete.
|
||||
* @throws AuthenticationException if authentication fails.
|
||||
*/
|
||||
@Override
|
||||
public Authentication attemptAuthentication(HttpServletRequest request,
|
||||
HttpServletResponse response) throws AuthenticationException {
|
||||
// 请求头中获取token信息
|
||||
String token = request.getParameter("id_token");
|
||||
String targetUrl = request.getParameter("target_url");
|
||||
if (ObjectUtils.isEmpty(token)) {
|
||||
throw new CustomException("id_token 参数不存在");
|
||||
}
|
||||
// 自定义数据源获取用户信息
|
||||
JwtAuthenticationToken authenticationToken = new JwtAuthenticationToken(token);
|
||||
authenticationToken.setTargetUrl(targetUrl);
|
||||
authenticationToken.setDetails(this.authenticationDetailsSource.buildDetails(request));
|
||||
return getAuthenticationManager().authenticate(authenticationToken);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
package com.zhgd.xmgl.sso.jwt;
|
||||
|
||||
import com.zhgd.xmgl.util.JwtUtils;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsChecker;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
|
||||
/**
|
||||
* @author SanLi
|
||||
* Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2023/7/26 17:19
|
||||
*/
|
||||
public final class JwtAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
public JwtAuthenticationProvider(UserDetailsService userDetailsService,
|
||||
UserDetailsChecker userDetailsChecker) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
this.userDetailsChecker = userDetailsChecker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs authentication with the same contract as
|
||||
* {@link AuthenticationManager#authenticate(Authentication)}
|
||||
* .
|
||||
*
|
||||
* @param authentication the authentication request object.
|
||||
* @return a fully authenticated object including credentials. May return
|
||||
* <code>null</code> if the <code>AuthenticationProvider</code> is unable to support
|
||||
* authentication of the passed <code>Authentication</code> object. In such a case,
|
||||
* the next <code>AuthenticationProvider</code> that supports the presented
|
||||
* <code>Authentication</code> class will be tried.
|
||||
* @throws AuthenticationException if authentication fails.
|
||||
*/
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
JwtAuthenticationToken authenticationToken = (JwtAuthenticationToken) authentication;
|
||||
// 公钥
|
||||
RSAPublicKey publicKey;
|
||||
try {
|
||||
ClassPathResource res = new ClassPathResource("sign.cer");
|
||||
publicKey = (RSAPublicKey) JwtUtils.readPublicKey(new String(
|
||||
FileCopyUtils.copyToByteArray(res.getInputStream()), StandardCharsets.UTF_8));
|
||||
} catch (CertificateException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String username = JwtUtils.getUserNameFromToken((String) authenticationToken.getPrincipal(),
|
||||
publicKey);
|
||||
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
||||
// userDetailsChecker.check(userDetails);
|
||||
return new UsernamePasswordAuthenticationToken(userDetails,
|
||||
userDetails.getPassword(), userDetails.getAuthorities());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if this <Code>AuthenticationProvider</code> supports the
|
||||
* indicated <Code>Authentication</code> object.
|
||||
* <p>
|
||||
* Returning <code>true</code> does not guarantee an
|
||||
* <code>AuthenticationProvider</code> will be able to authenticate the presented
|
||||
* instance of the <code>Authentication</code> class. It simply indicates it can
|
||||
* support closer evaluation of it. An <code>AuthenticationProvider</code> can still
|
||||
* return <code>null</code> from the {@link #authenticate(Authentication)} method to
|
||||
* indicate another <code>AuthenticationProvider</code> should be tried.
|
||||
* </p>
|
||||
* <p>
|
||||
* Selection of an <code>AuthenticationProvider</code> capable of performing
|
||||
* authentication is conducted at runtime the <code>ProviderManager</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param authentication
|
||||
* @return <code>true</code> if the implementation can more closely evaluate the
|
||||
* <code>Authentication</code> class presented
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return authentication.isAssignableFrom(JwtAuthenticationToken.class);
|
||||
}
|
||||
|
||||
private final UserDetailsService userDetailsService;
|
||||
|
||||
private final UserDetailsChecker userDetailsChecker;
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package com.zhgd.xmgl.sso.jwt;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author SanLi
|
||||
* Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2023/7/26 21:21
|
||||
*/
|
||||
public class JwtAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(JwtAuthenticationSuccessHandler.class);
|
||||
|
||||
/**
|
||||
* Called when a user has been successfully authenticated.
|
||||
*
|
||||
* @param request the request which caused the successful authentication
|
||||
* @param response the response
|
||||
* @param authentication the <tt>Authentication</tt> object which was created during
|
||||
* the authentication process.
|
||||
*/
|
||||
@Override
|
||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication authentication) throws IOException,
|
||||
ServletException {
|
||||
logger.info("JWT 单点登陆成功");
|
||||
// response.sendRedirect("/index");
|
||||
response.setHeader("Authorization", "Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJjc3F5MDAxIiwiaWF0IjoxNjk5NTgyNDE4LCJleHAiOjE2OTk2Njg4MTh9.awj6CmN2FprbRnlFhfJAsnudNXdE0HyOtldkU63zYa8");
|
||||
response.sendRedirect("http://jxjzw.zhgdyun.com:6080/#/home");
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package com.zhgd.xmgl.sso.jwt;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author SanLi
|
||||
* Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2023/7/26 17:19
|
||||
*/
|
||||
public class JwtAuthenticationToken extends AbstractAuthenticationToken {
|
||||
private final String idToken;
|
||||
|
||||
private String targetUrl;
|
||||
|
||||
/**
|
||||
* Creates a token with the supplied array of authorities.
|
||||
*/
|
||||
public JwtAuthenticationToken(String idToken) {
|
||||
super(new ArrayList<>());
|
||||
this.idToken = idToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* The credentials that prove the principal is correct. This is usually a password,
|
||||
* but could be anything relevant to the <code>AuthenticationManager</code>. Callers
|
||||
* are expected to populate the credentials.
|
||||
*
|
||||
* @return the credentials that prove the identity of the <code>Principal</code>
|
||||
*/
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* The identity of the principal being authenticated. In the case of an authentication
|
||||
* request with username and password, this would be the username. Callers are
|
||||
* expected to populate the principal for an authentication request.
|
||||
* <p>
|
||||
* The <tt>AuthenticationManager</tt> implementation will often return an
|
||||
* <tt>Authentication</tt> containing richer information as the principal for use by
|
||||
* the application. Many of the authentication providers will create a
|
||||
* {@code UserDetails} object as the principal.
|
||||
*
|
||||
* @return the <code>Principal</code> being authenticated or the authenticated
|
||||
* principal after authentication.
|
||||
*/
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return idToken;
|
||||
}
|
||||
|
||||
public void setTargetUrl(String targetUrl) {
|
||||
this.targetUrl = targetUrl;
|
||||
}
|
||||
|
||||
public String getTargetUrl() {
|
||||
return targetUrl;
|
||||
}
|
||||
}
|
||||
97
src/main/java/com/zhgd/xmgl/util/JwtUtils.java
Normal file
97
src/main/java/com/zhgd/xmgl/util/JwtUtils.java
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* eiam-application-jwt - Employee Identity and Access Management Program
|
||||
* Copyright © 2020-2023 TopIAM (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.zhgd.xmgl.util;
|
||||
|
||||
import io.jsonwebtoken.*;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Security;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* JWT 工具类
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/02/12 20:58
|
||||
*/
|
||||
@Slf4j
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class JwtUtils {
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
public static PublicKey readPublicKey(String pem) throws CertificateException {
|
||||
return getCertificate(pem.getBytes(StandardCharsets.UTF_8)).getPublicKey();
|
||||
}
|
||||
|
||||
public static X509Certificate getCertificate(byte[] der) throws CertificateException {
|
||||
CertificateFactory factory = CertificateFactory.getInstance("X.509");
|
||||
return (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(der));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 Token 获取用户名
|
||||
*
|
||||
* @param token {@link String} jwt token
|
||||
* @param publicKey {@link PublicKey} 公钥
|
||||
* @return {@link Claims}
|
||||
*/
|
||||
public static Claims validateToken(String token, PublicKey publicKey) {
|
||||
try {
|
||||
return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
|
||||
} catch (ExpiredJwtException e) {
|
||||
log.error("令牌过期", e);
|
||||
} catch (UnsupportedJwtException e) {
|
||||
log.error("处理令牌格式异常", e);
|
||||
} catch (MalformedJwtException e) {
|
||||
log.error("处理无效签名/claims", e);
|
||||
} catch (SignatureException e) {
|
||||
log.error("处理签名验证失败", e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.error("处理空令牌异常", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 Token 获取用户名
|
||||
*
|
||||
* @param token {@link String}
|
||||
* @param publicKey {@link PublicKey}
|
||||
* @return {@link String} 用户名
|
||||
*/
|
||||
public static String getUserNameFromToken(String token, PublicKey publicKey) {
|
||||
String username;
|
||||
try {
|
||||
Claims claims = validateToken(token, publicKey);
|
||||
username = claims.getSubject();
|
||||
} catch (Exception e) {
|
||||
username = null;
|
||||
}
|
||||
return username;
|
||||
}
|
||||
}
|
||||
22
src/main/resources/sign.cer
Normal file
22
src/main/resources/sign.cer
Normal file
@ -0,0 +1,22 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDkTCCAnmgAwIBAgIGAYu3V5DMMA0GCSqGSIb3DQEBCwUAMH8xLTArBgNVBAMM
|
||||
JGFwcF82cHFtcTM4aWdrb3VtN2tsd25wandtZDlhdnNnbmJ4YTEPMA0GA1UECgwG
|
||||
VG9wSUFNMQ4wDAYDVQQHDAVKaW5hbjERMA8GA1UECAwIU2hhbmRvbmcxCzAJBgNV
|
||||
BAYTAkNOMQ0wCwYDVQQLDARFSUFNMB4XDTIzMTExMDAzNDczMVoXDTMzMTExMDAz
|
||||
NDczMVowfzEtMCsGA1UEAwwkYXBwXzZwcW1xMzhpZ2tvdW03a2x3bnBqd21kOWF2
|
||||
c2duYnhhMQ8wDQYDVQQKDAZUb3BJQU0xDjAMBgNVBAcMBUppbmFuMREwDwYDVQQI
|
||||
DAhTaGFuZG9uZzELMAkGA1UEBhMCQ04xDTALBgNVBAsMBEVJQU0wggEiMA0GCSqG
|
||||
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDbfGd2/fJS4u2fLypMqkp4maCXPsJ1JS4G
|
||||
+rsHINagxX/csR88yxIPr68RdfEOHPX1l/71zyRXF7THGCZxeO/IeObw7hnlTu9O
|
||||
UKcUM+CE1w7OtBQ8GB67oO8RIrHJp0s3fm6n8kMc76LNV27blJWLSY15xm59d6bW
|
||||
ntiLs9z0bsGx0R9eMw7x6HvtIblrnceVIv6CZ/K6Bh1nxQTbF0UV/yDoJS+LD1dF
|
||||
nrMSWbpz6Ia6kmhVhgZeiCxeVYCn1ZzeQUcbc2GQ5OLMU8xUMzP29NzZZAVLWf1l
|
||||
AEuFmYgI+gZT/fkOFiivhz5p1Cp2qScM6o1IWWkfKd2riKloacLxAgMBAAGjEzAR
|
||||
MA8GA1UdEwQIMAYBAf8CAQEwDQYJKoZIhvcNAQELBQADggEBAEC/YI3+X7k+aw9X
|
||||
FHeZmNUPIX13w8a/AO0VGoNJZBBmFmnKtKIlIZjpJW01TpWt6y9u17tcARkmvSkm
|
||||
aI5A6z6lX2AoU17R2/slUYD8JD2XhN6yPAU0ZaxGAyBodIDNlC5qqwmSpJYMJo4w
|
||||
Uo9NqUaVdtwgrhBEJIDJEktT38DEzraXy3pIIowCUwGmlQ6Cj/dAVRAKRsLSl2i8
|
||||
QtCLsyA4AZODi1AmP1SXLlzT3mGGajZzOOUR1fSWlNGaEeYQ8fBklbMUw+uObFFP
|
||||
f/8HgmaCOdErJFNPrETla+34M/PkN/Wc3VHbL6DD8yXET3F8O96LdjQDkmlMNH69
|
||||
IseZyhE=
|
||||
-----END CERTIFICATE-----
|
||||
Loading…
x
Reference in New Issue
Block a user