Browse Source

first commit

tags/V2.2.0
chenjiandong 2 years ago
parent
commit
e2d908e694
12 changed files with 353 additions and 35 deletions
  1. +15
    -0
      tuoheng_oidc_server/pom.xml
  2. +14
    -15
      tuoheng_oidc_server/src/main/java/com/tuoheng/config/SecurityConfig.java
  3. +38
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/handler/AccessDeniedHandler.java
  4. +86
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/handler/AuthenticationEntryPoint.java
  5. +78
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/model/dto/JwtUser.java
  6. +2
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/model/dto/UserBaseInfoDto.java
  7. +29
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/service/impl/UserDetailsServiceImpl.java
  8. +3
    -0
      tuoheng_oidc_server/src/main/resources/application.yml
  9. +1
    -1
      tuoheng_oidc_server/src/main/resources/mapper/UserMapper.xml
  10. BIN
      tuoheng_oidc_server/src/main/resources/static/back.png
  11. BIN
      tuoheng_oidc_server/src/main/resources/static/form.png
  12. +87
    -19
      tuoheng_oidc_server/src/main/resources/templates/login.html

+ 15
- 0
tuoheng_oidc_server/pom.xml View File

@@ -88,6 +88,18 @@
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>

<!-- JSON 解析器和生成器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>

</dependencies>

@@ -133,6 +145,9 @@
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.*</include>
</includes>
</resource>

<resource>

+ 14
- 15
tuoheng_oidc_server/src/main/java/com/tuoheng/config/SecurityConfig.java View File

@@ -1,29 +1,26 @@
package com.tuoheng.config;

import com.tuoheng.handler.AccessDeniedHandler;
import com.tuoheng.handler.AuthenticationEntryPoint;
import com.tuoheng.mapper.UserMapper;
import com.tuoheng.model.dto.UserBaseInfoDto;
import com.tuoheng.service.OidcUserInfoService;
import com.tuoheng.service.impl.OidcUserInfoServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
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.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationContext;
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
@@ -76,8 +73,10 @@ public class SecurityConfig {
}).apply(authorizationServerConfigurer)
.and()
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
.exceptionHandling(exceptions -> exceptions.
authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")))
.exceptionHandling(exceptions -> exceptions
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
.accessDeniedHandler(new AccessDeniedHandler()))
//.authenticationEntryPoint(new AuthenticationEntryPoint()))
.apply(authorizationServerConfigurer)
.and()
.build();
@@ -90,6 +89,7 @@ public class SecurityConfig {
.authorizeHttpRequests((authorize) -> authorize
.antMatchers("/getHealth").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/static/**").permitAll()
.anyRequest().authenticated()
)
// Form login handles the redirect to the login page from the
@@ -104,14 +104,13 @@ public class SecurityConfig {

@Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.withDefaultPasswordEncoder()
/*UserDetails userDetails = User.withDefaultPasswordEncoder()
.username("admin")
.password("123456")
.roles("ADMIN")
.build();

return new InMemoryUserDetailsManager(userDetails);
//return new JdbcUserDetailsManager(dataSource);
return new InMemoryUserDetailsManager(userDetails);*/
return new JdbcUserDetailsManager(dataSource);
}

@Bean
@@ -119,9 +118,9 @@ public class SecurityConfig {
return ProviderSettings.builder().build();
}

// @Bean
// public PasswordEncoder passwordEncoder() {
// return new BCryptPasswordEncoder();
// }
/*@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}*/

}

+ 38
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/handler/AccessDeniedHandler.java View File

@@ -0,0 +1,38 @@
package com.tuoheng.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/10 11:55
*/

@Component
public class AccessDeniedHandler implements org.springframework.security.web.access.AccessDeniedHandler {

@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
response.setContentType("application/json;charset=UTF-8");
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", 401);
map.put("msg", "权限不足");
map.put("data", accessDeniedException.getMessage());
map.put("success", false);
map.put("path", request.getServletPath());
map.put("timestamp", String.valueOf(new Date().getTime()));
ObjectMapper mapper = new ObjectMapper();
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write(mapper.writeValueAsString(map));
}
}

+ 86
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/handler/AuthenticationEntryPoint.java View File

@@ -0,0 +1,86 @@
package com.tuoheng.handler;


import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.alibaba.fastjson.JSONObject;
import com.tuoheng.until.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.jwt.BadJwtException;
import org.springframework.security.oauth2.jwt.JwtValidationException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/10 12:00
*/
@Slf4j
public class AuthenticationEntryPoint implements org.springframework.security.web.AuthenticationEntryPoint {

@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
if (response.isCommitted()){
return;
}

Throwable throwable = authException.fillInStackTrace();

String errorMessage = "认证失败";

if (throwable instanceof BadCredentialsException){
errorMessage = "错误的客户端信息";
}else {
Throwable cause = authException.getCause();

if (cause instanceof JwtValidationException) {
log.warn("JWT Token 过期,具体内容:" + cause.getMessage());
errorMessage = "无效的token信息";
} else if (cause instanceof BadJwtException){
log.warn("JWT 签名异常,具体内容:" + cause.getMessage());
errorMessage = "无效的token信息";
} else if (cause instanceof AccountExpiredException){
errorMessage = "账户已过期";
} else if (cause instanceof LockedException){
errorMessage = "账户已被锁定";
// } else if (cause instanceof InvalidClientException || cause instanceof BadClientCredentialsException){
// response.getWriter().write(JSON.toJSONString(SingleResultBundle.failed(401,"无效的客户端")));
// } else if (cause instanceof InvalidGrantException || cause instanceof RedirectMismatchException){
// response.getWriter().write(JSON.toJSONString(SingleResultBundle.failed("无效的类型")));
// } else if (cause instanceof UnauthorizedClientException) {
// response.getWriter().write(JSON.toJSONString(SingleResultBundle.failed("未经授权的客户端")));
} else if (throwable instanceof InsufficientAuthenticationException) {
String message = throwable.getMessage();
if (message.contains("Invalid token does not contain resource id")){
errorMessage = "未经授权的资源服务器";
}else if (message.contains("Full authentication is required to access this resource")){
errorMessage = "缺少验证信息";
}
}else {
errorMessage = "验证异常";
}
}

response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setCharacterEncoding("utf-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
ObjectMapper objectMapper = new ObjectMapper();
JsonResult jsonResult = new JsonResult();
String resBody = objectMapper.writeValueAsString(JSONObject.toJSONString(jsonResult.error(401, errorMessage), SerializerFeature.WriteMapNullValue));
PrintWriter printWriter = response.getWriter();
printWriter.print(resBody);
printWriter.flush();
printWriter.close();
}
}

+ 78
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/model/dto/JwtUser.java View File

@@ -0,0 +1,78 @@
package com.tuoheng.model.dto;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.Collections;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/10 10:20
*/
public class JwtUser implements UserDetails {

private Integer id;
private String username;
private String password;
private Collection<? extends GrantedAuthority> authorities;

public JwtUser() {
}

// 写一个能直接使用user创建jwtUser的构造器
public JwtUser(UserBaseInfoDto user) {
id = user.getUserId();
username = user.getUserName();
password = user.getPassword();
authorities = Collections.singleton(new SimpleGrantedAuthority(user.getAuthority()));
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}

@Override
public String getPassword() {
return password;
}

@Override
public String getUsername() {
return 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;
}

@Override
public String toString() {
return "JwtUser{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", authorities=" + authorities +
'}';
}

}

+ 2
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/model/dto/UserBaseInfoDto.java View File

@@ -14,6 +14,8 @@ public class UserBaseInfoDto {

private String userName;

private String password;

private String authority;

}

+ 29
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/service/impl/UserDetailsServiceImpl.java View File

@@ -0,0 +1,29 @@
package com.tuoheng.service.impl;

import com.tuoheng.mapper.UserMapper;
import com.tuoheng.model.dto.JwtUser;
import com.tuoheng.model.dto.UserBaseInfoDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/10 10:13
*/
//@Service
//public class UserDetailsServiceImpl implements UserDetailsService {
// @Autowired
// UserMapper userMapper;
//
// @Override
// public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// // 根据用户名去数据库中查询
// UserBaseInfoDto userBaseInfoDto = userMapper.getUserBaseInfo(username);
// return new JwtUser(userBaseInfoDto);
// }
//
//}

+ 3
- 0
tuoheng_oidc_server/src/main/resources/application.yml View File

@@ -4,6 +4,9 @@ server:
spring:
profiles:
active: @package.environment@
web:
resources:
static-locations: classpath:/

mybatis:
mapper-locations: classpath*:mapper/*Mapper.xml

+ 1
- 1
tuoheng_oidc_server/src/main/resources/mapper/UserMapper.xml View File

@@ -9,7 +9,7 @@


<select id="getUserBaseInfo" resultType="com.tuoheng.model.dto.UserBaseInfoDto">
select a.id as userId, a.username as userName, b.authority
select a.id as userId, a.username as userName, a.password , b.authority
from users a
inner join authorities b on a.id = b.user_id
where a.username = #{username}

BIN
tuoheng_oidc_server/src/main/resources/static/back.png View File

Before After
Width: 2195  |  Height: 1080  |  Size: 596KB

BIN
tuoheng_oidc_server/src/main/resources/static/form.png View File

Before After
Width: 572  |  Height: 564  |  Size: 54KB

+ 87
- 19
tuoheng_oidc_server/src/main/resources/templates/login.html View File

@@ -1,27 +1,95 @@
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>login</title>
<style>
.login__back{
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: url('../static/back.png');
background-position: center center;
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
}
.login__form{
/* width: 460px;
height: 410px; */
width: 410px;
height: 350px;
background: url('../static/form.png');
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
padding: 60px 30px;
color: rgba(255, 255, 255, 1);
position: relative;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
.login__form h2{
font-size: 28px;
line-height: 25px;
text-align: center;
margin-bottom: 10px;
}
.login__form p{
font-size: 12px;
text-align: center;
margin-bottom: 36px;
}
.login__form form{
padding: 0 40px;
display: flex;
flex-direction: column;
background: transparent;
}
form input{
height: 40px;
margin-bottom: 18px;
border-radius: 6px;
color: #FFFFFF;
padding: 0 16px;
border: 1px solid rgba(255, 255, 255, 0.5);
background: transparent;
}
form input:focus-within{
outline: 0;
border: 1px solid #08EBFE;
}
button{
height: 46px;
border: none;
border-radius: 6px;
background: linear-gradient(0deg, #08EBFE 0%, #28BAC1 100%);
margin-top: 20px;
color: #FFFFFF;
font-size: 16px;
}
</style>
</head>
<body>
<h3>登录</h3>
<form th:action="@{/login}" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2">
<button type="submit">登录</button>
</td>
</tr>
</table>
</form>
<div class="login__back">
<div class="login__form">
<h2>拓恒统一登录平台</h2>
<p>TUOHENG LOGIN PLATFORM</p>
<form th:action="@{/login}" method="post">
<input name="username" placeholder="请输入用户名" type="text"/>
<input name="password" placeholder="请输入密码" type="password"/>
<!-- <input name="code" placeholder="请输入验证码" value="code" /> -->
<button type="submit">登 录</button>
</form>
</div>
</div>
</body>
</html>

Loading…
Cancel
Save