Initial commit
This commit is contained in:
33
cn-auth/.gitignore
vendored
Normal file
33
cn-auth/.gitignore
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
68
cn-auth/pom.xml
Normal file
68
cn-auth/pom.xml
Normal file
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.njcn.product</groupId>
|
||||
<artifactId>CN_Product</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cn-auth</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<name>cn-auth</name>
|
||||
<description>cn-auth</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>njcn-common</artifactId>
|
||||
<version>0.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>mybatis-plus</artifactId>
|
||||
<version>0.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>spingboot2.3.12</artifactId>
|
||||
<version>2.3.12</version>
|
||||
</dependency>
|
||||
<!-- Spring Security -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>32.1.3-jre</version> <!-- 使用最新稳定版 -->
|
||||
</dependency>
|
||||
|
||||
<!-- JWT -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>0.11.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.njcn.product.security;
|
||||
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import com.njcn.web.utils.HttpResultUtil;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@RestController
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class AuthController extends BaseController {
|
||||
|
||||
|
||||
private final AuthenticationManager authenticationManager;
|
||||
|
||||
|
||||
private final JwtUtil jwtUtil;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@PostMapping("/cn_authenticate")
|
||||
@ApiOperation("登录认证")
|
||||
public HttpResult<AuthResponse> createAuthenticationToken(@RequestBody @Validated AuthRequest authRequest) {
|
||||
String methodDescribe = getMethodDescribe("createAuthenticationToken");
|
||||
//log.info("Authentication request - username: {}, password: {}",authRequest.getUsername(),authRequest.getPassword());
|
||||
try {
|
||||
|
||||
// 执行认证,内部会调用 UserDetailsService 加载用户信息
|
||||
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authRequest.getUsername(),authRequest.getPassword()));
|
||||
|
||||
// 将认证信息存入 SecurityContext
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
// 直接从 Authentication 对象中获取已加载的 UserDetails,避免重复查询
|
||||
MyUserDetails userDetails = (MyUserDetails) authentication.getPrincipal();
|
||||
|
||||
// 获取用户部门(假设 CustomUserDetails 包含部门信息)
|
||||
String department = userDetails.getDeptId();
|
||||
|
||||
final String jwt = jwtUtil.generateToken(userDetails);
|
||||
|
||||
AuthResponse authResponse = new AuthResponse();
|
||||
authResponse.setToken(jwt);
|
||||
authResponse.setDeptId(department);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, authResponse, methodDescribe);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, null, methodDescribe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 认证请求类
|
||||
class AuthRequest {
|
||||
|
||||
@NotBlank(message = "用户名不可为空")
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.njcn.product.security;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Author: cdf
|
||||
* @CreateTime: 2025-06-26
|
||||
* @Description:
|
||||
*/
|
||||
@Data
|
||||
public class AuthResponse {
|
||||
|
||||
private String token;
|
||||
|
||||
private String deptId;
|
||||
|
||||
private String roleId;
|
||||
|
||||
private String userIndex;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.njcn.product.security;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class JwtRequestFilter extends OncePerRequestFilter {
|
||||
|
||||
private final UserDetailsService userDetailsService;
|
||||
private final JwtUtil jwtUtil;
|
||||
|
||||
public JwtRequestFilter(UserDetailsService userDetailsService, JwtUtil jwtUtil) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
this.jwtUtil = jwtUtil;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
final String authorizationHeader = request.getHeader("Authorization");
|
||||
String username = null;
|
||||
String jwt = null;
|
||||
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
|
||||
jwt = authorizationHeader.substring(7);
|
||||
try {
|
||||
username = jwtUtil.extractUsername(jwt);
|
||||
} catch (ExpiredJwtException e) {
|
||||
log.error(e.getMessage());
|
||||
sendErrorResponse(response,CommonResponseEnum.TOKEN_EXPIRE_JWT);
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
sendErrorResponse(response,CommonResponseEnum.PARSE_TOKEN_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
|
||||
|
||||
if (jwtUtil.validateToken(jwt, userDetails)) {
|
||||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
|
||||
new UsernamePasswordAuthenticationToken(
|
||||
userDetails, null, userDetails.getAuthorities());
|
||||
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
|
||||
}
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private void sendErrorResponse(HttpServletResponse response, CommonResponseEnum error) throws IOException {
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
|
||||
HttpResult<String> httpResult = new HttpResult<>();
|
||||
httpResult.setCode(error.getCode());
|
||||
httpResult.setMessage(error.getMessage());
|
||||
|
||||
response.getWriter().write(new JSONObject(httpResult, false).toString());
|
||||
}
|
||||
}
|
||||
85
cn-auth/src/main/java/com/njcn/product/security/JwtUtil.java
Normal file
85
cn-auth/src/main/java/com/njcn/product/security/JwtUtil.java
Normal file
@@ -0,0 +1,85 @@
|
||||
package com.njcn.product.security;
|
||||
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.security.Key;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Component
|
||||
public class JwtUtil {
|
||||
|
||||
private final String userId = "userId";
|
||||
private final String userName = "userName";
|
||||
private final String deptId = "deptId";
|
||||
|
||||
|
||||
private static final Key SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
|
||||
private static final long EXPIRATION_TIME = 1000 * 60 * 60 * 1000000000L; // 100000小时
|
||||
|
||||
// 生成JWT令牌
|
||||
public String generateToken(MyUserDetails userDetails) {
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put(userId,userDetails.getUserId());
|
||||
claims.put(userName,userDetails.getUsername());
|
||||
claims.put(deptId,userDetails.getDeptId());
|
||||
return createToken(claims, userDetails.getUsername());
|
||||
}
|
||||
|
||||
private String createToken(Map<String, Object> claims, String subject) {
|
||||
return Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.setSubject(subject)
|
||||
.setIssuedAt(new Date(System.currentTimeMillis()))
|
||||
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
|
||||
.signWith(SECRET_KEY, SignatureAlgorithm.HS256)
|
||||
.compact();
|
||||
}
|
||||
|
||||
// 验证令牌
|
||||
public Boolean validateToken(String token, UserDetails userDetails) {
|
||||
final String username = extractUsername(token);
|
||||
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
|
||||
}
|
||||
|
||||
// 提取用户名
|
||||
public String extractUsername(String token) {
|
||||
return extractClaim(token, it->it.get(userName).toString());
|
||||
}
|
||||
|
||||
// 提取用户ID
|
||||
public String extractUserId(String token) {
|
||||
return extractClaim(token,it->it.get(userId).toString());
|
||||
}
|
||||
|
||||
// 提取用户部门
|
||||
public String extractDepartment(String token) {
|
||||
return extractClaim(token, it->it.get(deptId).toString());
|
||||
}
|
||||
|
||||
// 提取过期时间
|
||||
public Date extractExpiration(String token) {
|
||||
return extractClaim(token, Claims::getExpiration);
|
||||
}
|
||||
|
||||
private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
|
||||
final Claims claims = extractAllClaims(token);
|
||||
return claimsResolver.apply(claims);
|
||||
}
|
||||
|
||||
private Claims extractAllClaims(String token) {
|
||||
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
|
||||
}
|
||||
|
||||
private Boolean isTokenExpired(String token) {
|
||||
return extractExpiration(token).before(new Date());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.njcn.product.security;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @Author: cdf
|
||||
* @CreateTime: 2025-06-26
|
||||
* @Description:
|
||||
*/
|
||||
@Data
|
||||
public class MyUserDetails implements UserDetails {
|
||||
|
||||
private String userId; // 用户唯一标识
|
||||
private String username; // 用户名
|
||||
private String password; // 密码
|
||||
private String deptId; // 部门信息
|
||||
private Collection<? extends GrantedAuthority> authorities; // 权限集合
|
||||
private boolean accountNonExpired; // 账户是否未过期
|
||||
private boolean accountNonLocked; // 账户是否未锁定
|
||||
private boolean credentialsNonExpired; // 凭证是否未过期
|
||||
private boolean enabled; // 账户是否启用
|
||||
|
||||
public MyUserDetails(String userId,String username, String password, String deptId,Collection<? extends GrantedAuthority> authorities) {
|
||||
this.userId = userId;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.deptId = deptId;
|
||||
this.authorities = authorities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.njcn.product.security;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MyUserDetailsService implements UserDetailsService {
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public MyUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
|
||||
|
||||
if("system_event".equals(username)){
|
||||
return new MyUserDetails("12345678910","system_event", "@#001njcnpqs","10001",
|
||||
new ArrayList<>());
|
||||
}
|
||||
|
||||
|
||||
// 这里应该从数据库中获取用户信息
|
||||
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
String encodedPassword = passwordEncoder.encode("@#001njcnpqs");
|
||||
/*
|
||||
LambdaQueryWrapper<PqsUser> userWrapper = new LambdaQueryWrapper<>();
|
||||
userWrapper.eq(PqsUser::getLoginname,username);
|
||||
PqsUser pqsUser = pqsUserMapper.selectOne(userWrapper);
|
||||
if(Objects.isNull(pqsUser)){
|
||||
throw new UsernameNotFoundException("User not found with username: " + username);
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<PqsUserSet> userSetWrapper = new LambdaQueryWrapper<>();
|
||||
userSetWrapper.eq(PqsUserSet::getUserIndex,pqsUser.getUserIndex());
|
||||
PqsUserSet pqsUserSet = pqsUserSetMapper.selectOne(userSetWrapper);
|
||||
String deptId = pqsUserSet.getDeptsIndex();*/
|
||||
|
||||
|
||||
return new MyUserDetails("12345678910","cdf", encodedPassword,"beac2cdb4acc4826d4519ea4177f3bfa",
|
||||
new ArrayList<>());
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.njcn.product.security;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Resource
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
@Resource
|
||||
private JwtRequestFilter jwtRequestFilter;
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf().disable()
|
||||
.authorizeRequests()
|
||||
//.antMatchers("/cn_authenticate","/ws/**","/accept/testEvent","/accept/eventMsg").permitAll() // 允许访问认证接口
|
||||
.antMatchers("/**").permitAll() // 允许访问认证接口
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 使用无状态会话
|
||||
|
||||
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user