统一认证

This commit is contained in:
pengjie 2023-11-10 18:46:04 +08:00
parent 621cc8f0c6
commit b921722c2a
18 changed files with 586 additions and 14 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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