feat: 开发中...
This commit is contained in:
@@ -0,0 +1,49 @@
|
|||||||
|
package cn.hamster3.application.blog.config;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class AuthTokenFilter extends OncePerRequestFilter {
|
||||||
|
@Resource(name = "userCache")
|
||||||
|
private Cache userCache;
|
||||||
|
|
||||||
|
public AuthTokenFilter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
String token = request.getHeader("token");
|
||||||
|
log.info("request token: {}", token);
|
||||||
|
if (token == null || token.isBlank()) {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UserDetails user = userCache.get(token, UserDetails.class);
|
||||||
|
if (user == null) {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SecurityContext context = SecurityContextHolder.getContext();
|
||||||
|
UsernamePasswordAuthenticationToken authentication = UsernamePasswordAuthenticationToken.authenticated(
|
||||||
|
user, "", user.getAuthorities()
|
||||||
|
);
|
||||||
|
context.setAuthentication(authentication);
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,13 +1,10 @@
|
|||||||
package cn.hamster3.application.blog.config;
|
package cn.hamster3.application.blog.config;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.cache.Cache;
|
||||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.ProviderManager;
|
import org.springframework.security.authentication.ProviderManager;
|
||||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||||
@@ -21,6 +18,11 @@ public class WebConfig {
|
|||||||
@Resource
|
@Resource
|
||||||
private UserDetailsService userDetailsService;
|
private UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
@Bean(name = "userCache")
|
||||||
|
public Cache getUserCache() {
|
||||||
|
return new ConcurrentMapCache("user-cache");
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public PasswordEncoder getPasswordEncoder() {
|
public PasswordEncoder getPasswordEncoder() {
|
||||||
return new BCryptPasswordEncoder(5);
|
return new BCryptPasswordEncoder(5);
|
||||||
@@ -35,12 +37,12 @@ public class WebConfig {
|
|||||||
return new ProviderManager(provider);
|
return new ProviderManager(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
// @Bean
|
||||||
@ConditionalOnMissingBean(ObjectMapper.class)
|
// @ConditionalOnMissingBean(ObjectMapper.class)
|
||||||
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
|
// public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
|
||||||
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
|
// ObjectMapper objectMapper = builder.createXmlMapper(false).build();
|
||||||
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
// objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||||
return objectMapper;
|
// return objectMapper;
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -2,23 +2,20 @@ package cn.hamster3.application.blog.config.security;
|
|||||||
|
|
||||||
import cn.hamster3.application.blog.entity.repo.UserRepository;
|
import cn.hamster3.application.blog.entity.repo.UserRepository;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
@Slf4j
|
@Service
|
||||||
@Component
|
|
||||||
public class BlogUserDetailService implements UserDetailsService {
|
public class BlogUserDetailService implements UserDetailsService {
|
||||||
@Resource
|
@Resource
|
||||||
private UserRepository userRepo;
|
private UserRepository userRepo;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
log.info("find user by email: {}", username);
|
|
||||||
return userRepo.findByEmailIgnoreCase(username)
|
return userRepo.findByEmailIgnoreCase(username)
|
||||||
.map(user -> new BlogUser(
|
.map(user -> new BlogUser(
|
||||||
user.getEmail(),
|
user.getEmail(),
|
||||||
|
@@ -21,7 +21,6 @@ public class SecurityConfig {
|
|||||||
.anyRequest().authenticated())
|
.anyRequest().authenticated())
|
||||||
.cors().and()
|
.cors().and()
|
||||||
.csrf().disable()
|
.csrf().disable()
|
||||||
.formLogin().and()
|
|
||||||
.httpBasic().and()
|
.httpBasic().and()
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@@ -29,8 +30,8 @@ public class UserController {
|
|||||||
|
|
||||||
@PostMapping("/login")
|
@PostMapping("/login")
|
||||||
@Operation(summary = "登录用户")
|
@Operation(summary = "登录用户")
|
||||||
public ResponseVO<Void> loginUser(@RequestBody @Valid UserLoginRequireVO requireVO) {
|
public ResponseVO<Void> loginUser(@RequestBody @Valid UserLoginRequireVO requireVO, HttpServletResponse response) {
|
||||||
return userService.loginUser(requireVO);
|
return userService.loginUser(requireVO, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/current")
|
@GetMapping("/current")
|
||||||
|
@@ -8,13 +8,14 @@ import cn.hamster3.application.blog.vo.user.UserCreateRequireVO;
|
|||||||
import cn.hamster3.application.blog.vo.user.UserInfoResponseVO;
|
import cn.hamster3.application.blog.vo.user.UserInfoResponseVO;
|
||||||
import cn.hamster3.application.blog.vo.user.UserLoginRequireVO;
|
import cn.hamster3.application.blog.vo.user.UserLoginRequireVO;
|
||||||
import cn.hamster3.application.blog.vo.user.UserUpdateRequireVO;
|
import cn.hamster3.application.blog.vo.user.UserUpdateRequireVO;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public interface IUserService {
|
public interface IUserService {
|
||||||
@NotNull ResponseVO<Void> loginUser(@NotNull UserLoginRequireVO requireVO);
|
@NotNull ResponseVO<Void> loginUser(@NotNull UserLoginRequireVO requireVO, @NotNull HttpServletResponse response);
|
||||||
|
|
||||||
@NotNull ResponseVO<UserInfoResponseVO> getCurrentUserInfo();
|
@NotNull ResponseVO<UserInfoResponseVO> getCurrentUserInfo();
|
||||||
|
|
||||||
|
@@ -20,9 +20,14 @@ import cn.hamster3.application.blog.vo.user.UserInfoResponseVO;
|
|||||||
import cn.hamster3.application.blog.vo.user.UserLoginRequireVO;
|
import cn.hamster3.application.blog.vo.user.UserLoginRequireVO;
|
||||||
import cn.hamster3.application.blog.vo.user.UserUpdateRequireVO;
|
import cn.hamster3.application.blog.vo.user.UserUpdateRequireVO;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -48,8 +53,23 @@ public class UserService implements IUserService {
|
|||||||
@Resource
|
@Resource
|
||||||
private AttachRepository attachRepo;
|
private AttachRepository attachRepo;
|
||||||
|
|
||||||
|
@Resource(name = "userCache")
|
||||||
|
private Cache userCache;
|
||||||
|
@Resource
|
||||||
|
private AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull ResponseVO<Void> loginUser(@NotNull UserLoginRequireVO requireVO) {
|
public @NotNull ResponseVO<Void> loginUser(@NotNull UserLoginRequireVO requireVO, @NotNull HttpServletResponse response) {
|
||||||
|
Authentication authenticate = authenticationManager.authenticate(
|
||||||
|
new UsernamePasswordAuthenticationToken(requireVO.getEmail(), requireVO.getPassword())
|
||||||
|
);
|
||||||
|
log.info("authenticate: {}", authenticate);
|
||||||
|
if (!authenticate.isAuthenticated()) {
|
||||||
|
return ResponseVO.failed("login failed.");
|
||||||
|
}
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
userCache.put(uuid.toString(), authenticate.getPrincipal());
|
||||||
|
response.addHeader("token", uuid.toString());
|
||||||
return ResponseVO.success();
|
return ResponseVO.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -14,7 +14,7 @@ import java.util.UUID;
|
|||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class BlogUtils {
|
public class BlogUtils {
|
||||||
@NotNull
|
@Nullable
|
||||||
public static Authentication getCurrentAuthentication() {
|
public static Authentication getCurrentAuthentication() {
|
||||||
return SecurityContextHolder.getContext().getAuthentication();
|
return SecurityContextHolder.getContext().getAuthentication();
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,10 @@ public class BlogUtils {
|
|||||||
public static Optional<BlogUser> getCurrentUser() {
|
public static Optional<BlogUser> getCurrentUser() {
|
||||||
Authentication authentication = getCurrentAuthentication();
|
Authentication authentication = getCurrentAuthentication();
|
||||||
log.info("==============================");
|
log.info("==============================");
|
||||||
|
if (authentication == null) {
|
||||||
|
log.info("current user authentication: null");
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
log.info("current user authentication: {}", authentication);
|
log.info("current user authentication: {}", authentication);
|
||||||
log.info("current user authentication getPrincipal: {}", authentication.getPrincipal());
|
log.info("current user authentication getPrincipal: {}", authentication.getPrincipal());
|
||||||
log.info("current user authentication getCredentials: {}", authentication.getCredentials());
|
log.info("current user authentication getCredentials: {}", authentication.getCredentials());
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package cn.hamster3.application.blog.vo;
|
package cn.hamster3.application.blog.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -7,6 +8,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
|
|
||||||
@Data
|
@Data
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
public class ResponseVO<T> {
|
public class ResponseVO<T> {
|
||||||
@NotNull
|
@NotNull
|
||||||
private Integer code;
|
private Integer code;
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
<link rel="icon" href="/favicon.ico">
|
<link rel="icon" href="/favicon.ico">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="description" content="叁只仓鼠的个人博客">
|
<meta name="description" content="叁只仓鼠的个人博客">
|
||||||
<title>Vite App</title>
|
<title>网站标题</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@@ -3,7 +3,7 @@ import { ref, onMounted } from 'vue';
|
|||||||
import { RouterLink, RouterView } from 'vue-router'
|
import { RouterLink, RouterView } from 'vue-router'
|
||||||
import { SettingControllerApiFactory, UserControllerApiFactory, UserInfoResponseVORoleEnum, type UserInfoResponseVO } from '@/api-base';
|
import { SettingControllerApiFactory, UserControllerApiFactory, UserInfoResponseVORoleEnum, type UserInfoResponseVO } from '@/api-base';
|
||||||
|
|
||||||
let title = ref<string>()
|
let title = ref<string>("网站标题")
|
||||||
let navigationMenu = ref([{
|
let navigationMenu = ref([{
|
||||||
url: "/about",
|
url: "/about",
|
||||||
text: "关于我"
|
text: "关于我"
|
||||||
@@ -14,10 +14,9 @@ onMounted(() => {
|
|||||||
SettingControllerApiFactory().getSettingContent("site.title")
|
SettingControllerApiFactory().getSettingContent("site.title")
|
||||||
.then(response => {
|
.then(response => {
|
||||||
let vo = response.data;
|
let vo = response.data;
|
||||||
title.value = vo.data ?? "网站标题"
|
let siteTitle: string = vo.data ?? "网站标题"
|
||||||
})
|
title.value = siteTitle
|
||||||
.catch(err => {
|
document.title = siteTitle
|
||||||
console.error(err);
|
|
||||||
})
|
})
|
||||||
|
|
||||||
SettingControllerApiFactory().getSettingContent("site.navigation.menus")
|
SettingControllerApiFactory().getSettingContent("site.navigation.menus")
|
||||||
@@ -28,14 +27,6 @@ onMounted(() => {
|
|||||||
navigationMenu.value = navigationMenuObject
|
navigationMenu.value = navigationMenuObject
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
UserControllerApiFactory().getCurrentUserInfo()
|
|
||||||
.then(response => {
|
|
||||||
let vo = response.data;
|
|
||||||
if (vo.code === 200) {
|
|
||||||
console.log(vo.data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@@ -13,6 +13,11 @@ const router = createRouter({
|
|||||||
name: 'register',
|
name: 'register',
|
||||||
component: () => import('@/views/RegisterView.vue')
|
component: () => import('@/views/RegisterView.vue')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/tags',
|
||||||
|
name: 'tags',
|
||||||
|
component: () => import('@/views/TagsView.vue')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: 'login',
|
name: 'login',
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
import { UserControllerApiFactory } from '@/api-base';
|
import { UserControllerApiFactory } from '@/api-base';
|
||||||
import type UserLoginRequireVO from '@/api-base';
|
import type UserLoginRequireVO from '@/api-base';
|
||||||
import { reactive } from 'vue';
|
import { reactive } from 'vue';
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
email: '',
|
email: '',
|
||||||
password: ''
|
password: ''
|
||||||
@@ -14,14 +15,13 @@ const onSubmit = () => {
|
|||||||
if (vo.data === 200) {
|
if (vo.data === 200) {
|
||||||
console.log('login success!')
|
console.log('login success!')
|
||||||
} else {
|
} else {
|
||||||
|
console.log('login failed!')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-alert title="error alert" type="error" show-icon />
|
|
||||||
<div class="login-div">
|
<div class="login-div">
|
||||||
<el-card>
|
<el-card>
|
||||||
<el-form :model="form" label-width="60px">
|
<el-form :model="form" label-width="60px">
|
||||||
|
13
blog-frontend/src/views/TagsView.vue
Normal file
13
blog-frontend/src/views/TagsView.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
Reference in New Issue
Block a user