mirror of
https://gitee.com/devlive-community/datacap.git
synced 2024-11-30 11:07:41 +08:00
Feature security (#94)
This commit is contained in:
commit
4a8ae1c92e
1
pom.xml
1
pom.xml
@ -259,6 +259,7 @@
|
||||
</tag>
|
||||
</tags>
|
||||
<additionalparam>-Xdoclint:none</additionalparam>
|
||||
<failOnError>false</failOnError>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
|
@ -15,6 +15,7 @@
|
||||
<properties>
|
||||
<mysql.version>8.0.28</mysql.version>
|
||||
<h2.version>2.1.214</h2.version>
|
||||
<jjwt.version>0.9.1</jjwt.version>
|
||||
<findbugs.version>3.0.1</findbugs.version>
|
||||
<frontend-maven-plugin.version>1.12.1</frontend-maven-plugin.version>
|
||||
<node.version>v16.10.0</node.version>
|
||||
@ -45,6 +46,16 @@
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>${jjwt.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
|
@ -3,3 +3,6 @@ server.port=9096
|
||||
spring.datasource.url=jdbc:mysql://localhost:3306/datacap?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false
|
||||
spring.datasource.username=root
|
||||
spring.datasource.password=12345678
|
||||
|
||||
datacap.security.secret=DataCapSecretKey
|
||||
datacap.security.expiration=86400000
|
||||
|
@ -0,0 +1,29 @@
|
||||
package io.edurt.datacap.server.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class JwtResponse
|
||||
{
|
||||
private String token;
|
||||
private String type = "Bearer";
|
||||
private Long id;
|
||||
private String username;
|
||||
private List<String> roles;
|
||||
|
||||
public JwtResponse(String accessToken, Long id, String username, List<String> roles)
|
||||
{
|
||||
this.token = accessToken;
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.roles = roles;
|
||||
}
|
||||
}
|
@ -7,7 +7,12 @@ public enum ServiceState
|
||||
PLUGIN_EXECUTE_FAILED(2002, "Plugin execute failed"),
|
||||
REQUEST_VALID_ARGUMENT(3001, "The related parameters cannot be verified"),
|
||||
REQUEST_VALID_ARGUMENT_FORMAT(3002, "Unable to format related parameters"),
|
||||
REQUEST_VALID_ARGUMENT_LAYOUT(3003, "Related parameters cannot be resolved");
|
||||
REQUEST_VALID_ARGUMENT_LAYOUT(3003, "Related parameters cannot be resolved"),
|
||||
USER_NOT_FOUND(4001, "User dose not exists"),
|
||||
USER_ROLE_NOT_FOUND(4002, "User role dose not exists"),
|
||||
USER_UNAUTHORIZED(4003, "Insufficient current user permissions"),
|
||||
USER_EXISTS(4004, "User exists"),
|
||||
USER_BAD_CREDENTIALS(4005, "The account or password is incorrect");
|
||||
|
||||
private Integer code;
|
||||
private String value;
|
||||
|
@ -0,0 +1,76 @@
|
||||
package io.edurt.datacap.server.configure;
|
||||
|
||||
import io.edurt.datacap.server.security.AuthTokenFilterService;
|
||||
import io.edurt.datacap.server.security.JwtAuthEntryPoint;
|
||||
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.method.configuration.EnableGlobalMethodSecurity;
|
||||
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;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(
|
||||
// securedEnabled = true,
|
||||
// jsr250Enabled = true,
|
||||
prePostEnabled = true)
|
||||
public class SecurityConfigure
|
||||
extends WebSecurityConfigurerAdapter
|
||||
{
|
||||
private final UserDetailsService userDetailsService;
|
||||
private final JwtAuthEntryPoint unauthorizedHandler;
|
||||
|
||||
public SecurityConfigure(UserDetailsService userDetailsService, JwtAuthEntryPoint unauthorizedHandler)
|
||||
{
|
||||
this.userDetailsService = userDetailsService;
|
||||
this.unauthorizedHandler = unauthorizedHandler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthTokenFilterService authenticationJwtTokenFilter()
|
||||
{
|
||||
return new AuthTokenFilterService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder)
|
||||
throws Exception
|
||||
{
|
||||
authenticationManagerBuilder.userDetailsService(userDetailsService)
|
||||
.passwordEncoder(passwordEncoder());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception
|
||||
{
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder()
|
||||
{
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http)
|
||||
throws Exception
|
||||
{
|
||||
http.cors().and().csrf().disable()
|
||||
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
|
||||
.authorizeRequests().antMatchers("/api/auth/**").permitAll()
|
||||
.anyRequest().authenticated();
|
||||
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package io.edurt.datacap.server.controller;
|
||||
|
||||
import io.edurt.datacap.server.common.JwtResponse;
|
||||
import io.edurt.datacap.server.common.Response;
|
||||
import io.edurt.datacap.server.entity.UserEntity;
|
||||
import io.edurt.datacap.server.service.UserService;
|
||||
import io.edurt.datacap.server.validation.ValidationGroup;
|
||||
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.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/auth")
|
||||
public class AuthController
|
||||
{
|
||||
private final UserService userService;
|
||||
|
||||
public AuthController(UserService userService)
|
||||
{
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@PostMapping("/signin")
|
||||
public Response<JwtResponse> authenticateUser(@RequestBody @Validated(ValidationGroup.Crud.Auth.class) UserEntity configure)
|
||||
{
|
||||
return this.userService.authenticate(configure);
|
||||
}
|
||||
|
||||
@PostMapping("/signup")
|
||||
public Response<?> registerUser(@RequestBody @Validated(ValidationGroup.Crud.Create.class) UserEntity configure)
|
||||
{
|
||||
return this.userService.saveOrUpdate(configure);
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package io.edurt.datacap.server.entity;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
@Data
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Entity
|
||||
@Table(name = "role")
|
||||
@org.hibernate.annotations.Table(appliesTo = "role", comment = "User rights configuration table")
|
||||
@SuppressFBWarnings(value = {"EI_EXPOSE_REP"},
|
||||
justification = "I prefer to suppress these FindBugs warnings")
|
||||
public class RoleEntity
|
||||
{
|
||||
@Id()
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
@Column(name = "description", columnDefinition = "varchar(1000)")
|
||||
private String description;
|
||||
|
||||
@Column(name = "create_time", columnDefinition = "datetime(5) default CURRENT_TIMESTAMP()")
|
||||
private Timestamp createTime;
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package io.edurt.datacap.server.entity;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import io.edurt.datacap.server.validation.ValidationGroup;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "users",
|
||||
uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = "username")
|
||||
})
|
||||
@SuppressFBWarnings(value = {"EI_EXPOSE_REP"},
|
||||
justification = "I prefer to suppress these FindBugs warnings")
|
||||
public class UserEntity
|
||||
{
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@NotBlank(groups = {
|
||||
ValidationGroup.Crud.Create.class,
|
||||
ValidationGroup.Crud.Update.class,
|
||||
ValidationGroup.Crud.Auth.class
|
||||
})
|
||||
@Size(max = 20)
|
||||
@Column(name = "username")
|
||||
private String username;
|
||||
|
||||
@NotBlank(groups = {
|
||||
ValidationGroup.Crud.Create.class,
|
||||
ValidationGroup.Crud.Update.class,
|
||||
ValidationGroup.Crud.Auth.class
|
||||
})
|
||||
@Size(max = 120)
|
||||
@Column(name = "password")
|
||||
private String password;
|
||||
|
||||
@Column(name = "create_time", columnDefinition = "datetime(5) default CURRENT_TIMESTAMP()")
|
||||
private Timestamp createTime;
|
||||
|
||||
@ManyToMany(fetch = FetchType.LAZY)
|
||||
@JoinTable(name = "user_roles",
|
||||
joinColumns = @JoinColumn(name = "user_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "role_id"))
|
||||
private Set<RoleEntity> roles = new HashSet<>();
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package io.edurt.datacap.server.repository;
|
||||
|
||||
import io.edurt.datacap.server.entity.RoleEntity;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface RoleRepository
|
||||
extends PagingAndSortingRepository<RoleEntity, Long>
|
||||
{
|
||||
Optional<RoleEntity> findByName(String name);
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package io.edurt.datacap.server.repository;
|
||||
|
||||
import io.edurt.datacap.server.entity.UserEntity;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface UserRepository
|
||||
extends PagingAndSortingRepository<UserEntity, Long>
|
||||
{
|
||||
Optional<UserEntity> findByUsername(String username);
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package io.edurt.datacap.server.security;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.util.StringUtils;
|
||||
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;
|
||||
|
||||
@Slf4j
|
||||
public class AuthTokenFilterService
|
||||
extends OncePerRequestFilter
|
||||
{
|
||||
@Autowired
|
||||
private JwtService jwtService;
|
||||
|
||||
@Autowired
|
||||
private UserDetailsServiceImpl userDetailsService;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
try {
|
||||
String jwt = parseJwt(request);
|
||||
if (jwt != null && jwtService.validateJwtToken(jwt)) {
|
||||
String username = jwtService.getUserNameFromJwtToken(jwt);
|
||||
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
||||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
|
||||
userDetails, null, userDetails.getAuthorities());
|
||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("Cannot set user authentication: {}", e);
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private String parseJwt(HttpServletRequest request)
|
||||
{
|
||||
String headerAuth = request.getHeader("Authorization");
|
||||
|
||||
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
|
||||
return headerAuth.substring(7);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package io.edurt.datacap.server.security;
|
||||
|
||||
import io.edurt.datacap.server.common.JSON;
|
||||
import io.edurt.datacap.server.common.Response;
|
||||
import io.edurt.datacap.server.common.ServiceState;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class JwtAuthEntryPoint
|
||||
implements AuthenticationEntryPoint
|
||||
{
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException authException)
|
||||
throws IOException
|
||||
{
|
||||
log.error("Unauthorized error: {}", authException.getMessage());
|
||||
if (authException instanceof BadCredentialsException) {
|
||||
response.getWriter().print(JSON.toJSON(Response.failure(ServiceState.USER_BAD_CREDENTIALS)));
|
||||
}
|
||||
else {
|
||||
response.getWriter().print(JSON.toJSON(Response.failure(ServiceState.USER_UNAUTHORIZED)));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package io.edurt.datacap.server.security;
|
||||
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.MalformedJwtException;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class JwtService
|
||||
{
|
||||
@Value("${datacap.security.secret}")
|
||||
private String jwtSecret = "DataCapSecretKey";
|
||||
|
||||
@Value("${datacap.security.expiration}")
|
||||
private int jwtExpirationMs = 86400000;
|
||||
|
||||
public String generateJwtToken(Authentication authentication)
|
||||
{
|
||||
UserDetailsService userPrincipal = (UserDetailsService) authentication.getPrincipal();
|
||||
return Jwts.builder()
|
||||
.setSubject((userPrincipal.getUsername()))
|
||||
.setIssuedAt(new Date())
|
||||
.setExpiration(new Date((new Date()).getTime() + jwtExpirationMs))
|
||||
.signWith(SignatureAlgorithm.HS512, jwtSecret)
|
||||
.compact();
|
||||
}
|
||||
|
||||
public String getUserNameFromJwtToken(String token)
|
||||
{
|
||||
return Jwts.parser()
|
||||
.setSigningKey(jwtSecret)
|
||||
.parseClaimsJws(token)
|
||||
.getBody()
|
||||
.getSubject();
|
||||
}
|
||||
|
||||
public boolean validateJwtToken(String authToken)
|
||||
{
|
||||
try {
|
||||
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
|
||||
return true;
|
||||
}
|
||||
catch (SignatureException e) {
|
||||
log.error("Invalid JWT signature: {}", e.getMessage());
|
||||
}
|
||||
catch (MalformedJwtException e) {
|
||||
log.error("Invalid JWT token: {}", e.getMessage());
|
||||
}
|
||||
catch (ExpiredJwtException e) {
|
||||
log.error("JWT token is expired: {}", e.getMessage());
|
||||
}
|
||||
catch (UnsupportedJwtException e) {
|
||||
log.error("JWT token is unsupported: {}", e.getMessage());
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
log.error("JWT claims string is empty: {}", e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package io.edurt.datacap.server.security;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.edurt.datacap.server.entity.UserEntity;
|
||||
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.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class UserDetailsService
|
||||
implements UserDetails
|
||||
{
|
||||
private Long id;
|
||||
private String username;
|
||||
@JsonIgnore
|
||||
private String password;
|
||||
private Collection<? extends GrantedAuthority> authorities;
|
||||
|
||||
public UserDetailsService(Long id, String username, String password,
|
||||
Collection<? extends GrantedAuthority> authorities)
|
||||
{
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.authorities = authorities;
|
||||
}
|
||||
|
||||
public static UserDetailsService build(UserEntity user)
|
||||
{
|
||||
List<GrantedAuthority> authorities = user.getRoles().stream()
|
||||
.map(role -> new SimpleGrantedAuthority(role.getName()))
|
||||
.collect(Collectors.toList());
|
||||
return new UserDetailsService(
|
||||
user.getId(),
|
||||
user.getUsername(),
|
||||
user.getPassword(),
|
||||
authorities);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
public Long getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package io.edurt.datacap.server.security;
|
||||
|
||||
import io.edurt.datacap.server.entity.UserEntity;
|
||||
import io.edurt.datacap.server.repository.UserRepository;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
@Service
|
||||
public class UserDetailsServiceImpl
|
||||
implements org.springframework.security.core.userdetails.UserDetailsService
|
||||
{
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public UserDetailsServiceImpl(UserRepository userRepository)
|
||||
{
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public UserDetails loadUserByUsername(String username)
|
||||
throws UsernameNotFoundException
|
||||
{
|
||||
UserEntity user = userRepository.findByUsername(username)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username));
|
||||
return UserDetailsService.build(user);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package io.edurt.datacap.server.service;
|
||||
|
||||
import io.edurt.datacap.server.common.JwtResponse;
|
||||
import io.edurt.datacap.server.common.Response;
|
||||
import io.edurt.datacap.server.entity.UserEntity;
|
||||
|
||||
public interface UserService
|
||||
{
|
||||
Response<UserEntity> saveOrUpdate(UserEntity configure);
|
||||
|
||||
Response<JwtResponse> authenticate(UserEntity configure);
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package io.edurt.datacap.server.service.impl;
|
||||
|
||||
import io.edurt.datacap.server.common.JwtResponse;
|
||||
import io.edurt.datacap.server.common.Response;
|
||||
import io.edurt.datacap.server.common.ServiceState;
|
||||
import io.edurt.datacap.server.entity.RoleEntity;
|
||||
import io.edurt.datacap.server.entity.UserEntity;
|
||||
import io.edurt.datacap.server.repository.RoleRepository;
|
||||
import io.edurt.datacap.server.repository.UserRepository;
|
||||
import io.edurt.datacap.server.security.JwtService;
|
||||
import io.edurt.datacap.server.security.UserDetailsService;
|
||||
import io.edurt.datacap.server.service.UserService;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
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.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class UserServiceImpl
|
||||
implements UserService
|
||||
{
|
||||
private final UserRepository userRepository;
|
||||
private final RoleRepository roleRepository;
|
||||
private final PasswordEncoder encoder;
|
||||
private final AuthenticationManager authenticationManager;
|
||||
private final JwtService jwtService;
|
||||
|
||||
public UserServiceImpl(UserRepository userRepository, RoleRepository roleRepository, PasswordEncoder encoder, AuthenticationManager authenticationManager, JwtService jwtService)
|
||||
{
|
||||
this.userRepository = userRepository;
|
||||
this.roleRepository = roleRepository;
|
||||
this.encoder = encoder;
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.jwtService = jwtService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<UserEntity> saveOrUpdate(UserEntity configure)
|
||||
{
|
||||
Optional<UserEntity> userOptional = this.userRepository.findByUsername(configure.getUsername());
|
||||
if (userOptional.isPresent()) {
|
||||
return Response.failure(ServiceState.USER_EXISTS);
|
||||
}
|
||||
|
||||
UserEntity user = new UserEntity();
|
||||
user.setUsername(configure.getUsername());
|
||||
user.setPassword(encoder.encode(configure.getPassword()));
|
||||
|
||||
Set<RoleEntity> userRoles = configure.getRoles();
|
||||
Set<RoleEntity> roles = new HashSet<>();
|
||||
if (ObjectUtils.isEmpty(userRoles)) {
|
||||
Optional<RoleEntity> userRoleOptional = roleRepository.findByName("User");
|
||||
if (!userRoleOptional.isPresent()) {
|
||||
return Response.failure(ServiceState.USER_ROLE_NOT_FOUND);
|
||||
}
|
||||
roles.add(userRoleOptional.get());
|
||||
}
|
||||
user.setRoles(roles);
|
||||
return Response.success(userRepository.save(user));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<JwtResponse> authenticate(UserEntity configure)
|
||||
{
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(configure.getUsername(), configure.getPassword());
|
||||
Authentication authentication = authenticationManager.authenticate(authenticationToken);
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
String jwt = jwtService.generateJwtToken(authentication);
|
||||
|
||||
UserDetailsService userDetails = (UserDetailsService) authentication.getPrincipal();
|
||||
List<String> roles = userDetails.getAuthorities().stream()
|
||||
.map(item -> item.getAuthority())
|
||||
.collect(Collectors.toList());
|
||||
return Response.success(new JwtResponse(jwt, userDetails.getId(), userDetails.getUsername(), roles));
|
||||
}
|
||||
}
|
@ -23,5 +23,9 @@ public interface ValidationGroup
|
||||
interface Delete
|
||||
extends Crud
|
||||
{}
|
||||
|
||||
interface Auth
|
||||
extends Crud
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,38 @@
|
||||
ALTER TABLE `datacap`.`source`
|
||||
ADD COLUMN `_ssl` boolean default false;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `datacap`.`users`
|
||||
(
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`username` varchar(255) DEFAULT NULL COMMENT ' ',
|
||||
`password` varchar(255) DEFAULT NULL COMMENT ' ',
|
||||
`create_time` datetime(5) DEFAULT CURRENT_TIMESTAMP(5),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARSET = utf8;
|
||||
|
||||
INSERT INTO `datacap`.`users` (`id`, `username`, `password`)
|
||||
VALUES (1, 'admin', '$2a$10$dNGjVHzGLnI7MA50iiD8V.LFPPYetB/04eTiZm8ULaso/BaKV.RS.'),
|
||||
(2, 'datacap', '$2a$10$o3h5jWxwNzxkWyP4wPXz4eoduNkQpF7eaCLStw2VYlTU2BUbed0Di');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `datacap`.`role`
|
||||
(
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) DEFAULT NULL COMMENT ' ',
|
||||
`description` varchar(255) DEFAULT NULL COMMENT ' ',
|
||||
`create_time` datetime(5) DEFAULT CURRENT_TIMESTAMP(5),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARSET = utf8;
|
||||
|
||||
INSERT INTO `datacap`.`role`(`name`, `description`)
|
||||
VALUES ('Admin', 'Admin role'),
|
||||
('User', 'User role');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `datacap`.`user_roles`
|
||||
(
|
||||
`user_id` bigint(20) NOT NULL,
|
||||
`role_id` bigint(20) NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO `datacap`.`user_roles` (`user_id`, `role_id`)
|
||||
VALUES (1, 1),
|
||||
(2, 2);
|
||||
|
3
web/console-fe/src/common/Common.ts
Normal file
3
web/console-fe/src/common/Common.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
token: 'AuthToken'
|
||||
}
|
@ -1,27 +1,37 @@
|
||||
import { ResponseModel } from "@/model/ResponseModel";
|
||||
import { message } from "ant-design-vue";
|
||||
import {ResponseModel} from "@/model/ResponseModel";
|
||||
import {message} from "ant-design-vue";
|
||||
import axios from 'axios';
|
||||
import Common from "@/common/Common";
|
||||
|
||||
axios.defaults.headers.post['Access-Control-Allow-Origin'] = '*';
|
||||
|
||||
const configure = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
cancelToken: undefined
|
||||
}
|
||||
export class HttpCommon
|
||||
{
|
||||
private configure;
|
||||
|
||||
export class HttpCommon {
|
||||
constructor() {
|
||||
constructor()
|
||||
{
|
||||
if (process.env.NODE_ENV === 'development' ||
|
||||
window.location.hostname === 'localhost') {
|
||||
axios.defaults.baseURL = 'http://localhost:9096';
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
axios.defaults.baseURL = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port : '');
|
||||
}
|
||||
|
||||
const auth = JSON.parse(localStorage.getItem(Common.token) || '{}');
|
||||
this.configure = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': auth.type + ' ' + auth.token
|
||||
},
|
||||
cancelToken: undefined,
|
||||
params: undefined
|
||||
}
|
||||
}
|
||||
|
||||
handlerSuccessful(result: any): ResponseModel {
|
||||
handlerSuccessful(result: any): ResponseModel
|
||||
{
|
||||
const data = result.data;
|
||||
let message = data.message;
|
||||
if (data.message instanceof Array) {
|
||||
@ -40,7 +50,8 @@ export class HttpCommon {
|
||||
return response;
|
||||
}
|
||||
|
||||
handlerFailed(error: any): ResponseModel {
|
||||
handlerFailed(error: any): ResponseModel
|
||||
{
|
||||
const response: ResponseModel = {
|
||||
code: 0,
|
||||
message: error.message,
|
||||
@ -49,9 +60,11 @@ export class HttpCommon {
|
||||
return response;
|
||||
}
|
||||
|
||||
get(url: string, params?: any): Promise<ResponseModel> {
|
||||
get(url: string, params?: any): Promise<ResponseModel>
|
||||
{
|
||||
return new Promise((resolve) => {
|
||||
axios.get(url, { params: params })
|
||||
this.configure.params = params;
|
||||
axios.get(url, this.configure)
|
||||
.then(result => {
|
||||
resolve(this.handlerSuccessful(result));
|
||||
}, error => {
|
||||
@ -61,10 +74,11 @@ export class HttpCommon {
|
||||
});
|
||||
}
|
||||
|
||||
post(url: string, data = {}, cancelToken?: any): Promise<ResponseModel> {
|
||||
post(url: string, data = {}, cancelToken?: any): Promise<ResponseModel>
|
||||
{
|
||||
return new Promise((resolve) => {
|
||||
configure.cancelToken = cancelToken;
|
||||
axios.post(url, data, configure)
|
||||
this.configure.cancelToken = cancelToken;
|
||||
axios.post(url, data, this.configure)
|
||||
.then(result => {
|
||||
resolve(this.handlerSuccessful(result));
|
||||
}, error => {
|
||||
@ -74,9 +88,10 @@ export class HttpCommon {
|
||||
});
|
||||
}
|
||||
|
||||
put(url: string, data = {}): Promise<ResponseModel> {
|
||||
put(url: string, data = {}): Promise<ResponseModel>
|
||||
{
|
||||
return new Promise((resolve) => {
|
||||
axios.put(url, data, configure)
|
||||
axios.put(url, data, this.configure)
|
||||
.then(result => {
|
||||
resolve(this.handlerSuccessful(result));
|
||||
}, error => {
|
||||
@ -86,9 +101,10 @@ export class HttpCommon {
|
||||
});
|
||||
}
|
||||
|
||||
delete(url: string): Promise<ResponseModel> {
|
||||
delete(url: string): Promise<ResponseModel>
|
||||
{
|
||||
return new Promise((resolve) => {
|
||||
axios.delete(url)
|
||||
axios.delete(url, this.configure)
|
||||
.then(result => {
|
||||
resolve(this.handlerSuccessful(result));
|
||||
}, error => {
|
||||
@ -98,7 +114,8 @@ export class HttpCommon {
|
||||
});
|
||||
}
|
||||
|
||||
getAxios() {
|
||||
getAxios()
|
||||
{
|
||||
return axios;
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
const en = {
|
||||
common: {
|
||||
home: 'Home',
|
||||
query: 'Query',
|
||||
admin: 'Admin',
|
||||
source: 'Source',
|
||||
history: 'History'
|
||||
}
|
||||
}
|
||||
|
||||
export default en;
|
14
web/console-fe/src/i18n/langs/en/common.ts
Normal file
14
web/console-fe/src/i18n/langs/en/common.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export default {
|
||||
home: 'Home',
|
||||
query: 'Query',
|
||||
admin: 'Admin',
|
||||
source: 'DataSource',
|
||||
history: 'History',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
login: 'Login',
|
||||
logout: 'Logout',
|
||||
english: 'English',
|
||||
chinese: 'Chinese',
|
||||
register: 'Register'
|
||||
}
|
9
web/console-fe/src/i18n/langs/en/index.ts
Normal file
9
web/console-fe/src/i18n/langs/en/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import enGB from 'ant-design-vue/es/locale/en_GB'
|
||||
import common from "@/i18n/langs/en/common";
|
||||
import required from "@/i18n/langs/en/required";
|
||||
|
||||
export default {
|
||||
...enGB,
|
||||
common: common,
|
||||
required: required
|
||||
}
|
4
web/console-fe/src/i18n/langs/en/required.ts
Normal file
4
web/console-fe/src/i18n/langs/en/required.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export default {
|
||||
username: 'Please input your username!',
|
||||
password: 'Please input your password!'
|
||||
}
|
@ -1,15 +1,7 @@
|
||||
import en from "@/i18n/langs/en";
|
||||
import zhCn from "@/i18n/langs/zhCn";
|
||||
import enGB from 'ant-design-vue/es/locale/en_GB';
|
||||
import zhCN from 'ant-design-vue/es/locale/zh_CN';
|
||||
import en from "@/i18n/langs/en/index";
|
||||
import zhCn from "@/i18n/langs/zhCn/index";
|
||||
|
||||
export default {
|
||||
en: {
|
||||
...en,
|
||||
...enGB,
|
||||
},
|
||||
zh_cn: {
|
||||
...zhCn,
|
||||
...zhCN,
|
||||
}
|
||||
en: en,
|
||||
zh_cn: zhCn
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
const zhCn = {
|
||||
common: {
|
||||
home: '主页',
|
||||
query: '查询',
|
||||
admin: '管理',
|
||||
source: '源',
|
||||
history: '历史'
|
||||
}
|
||||
}
|
||||
|
||||
export default zhCn;
|
14
web/console-fe/src/i18n/langs/zhCn/common.ts
Normal file
14
web/console-fe/src/i18n/langs/zhCn/common.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export default {
|
||||
home: '主页',
|
||||
query: '查询',
|
||||
admin: '管理',
|
||||
source: '数据源',
|
||||
history: '历史',
|
||||
username: '用户名',
|
||||
password: '密码',
|
||||
login: '登录',
|
||||
logout: '注销',
|
||||
english: '英语',
|
||||
chinese: '中文',
|
||||
register: '注册'
|
||||
}
|
9
web/console-fe/src/i18n/langs/zhCn/index.ts
Normal file
9
web/console-fe/src/i18n/langs/zhCn/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import zhCN from 'ant-design-vue/es/locale/zh_CN';
|
||||
import common from "@/i18n/langs/zhCn/common";
|
||||
import required from "@/i18n/langs/zhCn/required";
|
||||
|
||||
export default {
|
||||
...zhCN,
|
||||
common: common,
|
||||
required: required
|
||||
}
|
4
web/console-fe/src/i18n/langs/zhCn/required.ts
Normal file
4
web/console-fe/src/i18n/langs/zhCn/required.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export default {
|
||||
username: '请输入用户名!',
|
||||
password: '请输入密码!'
|
||||
}
|
8
web/console-fe/src/model/AuthResponse.ts
Normal file
8
web/console-fe/src/model/AuthResponse.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export interface AuthResponse
|
||||
{
|
||||
token: string;
|
||||
type: string;
|
||||
id: number;
|
||||
username: string;
|
||||
roles: [];
|
||||
}
|
8
web/console-fe/src/model/AuthUser.ts
Normal file
8
web/console-fe/src/model/AuthUser.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export interface AuthUser
|
||||
{
|
||||
username: string;
|
||||
password: string;
|
||||
// Marks the error message returned after an operation
|
||||
message?: string;
|
||||
loading?: boolean;
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
import LayoutContainer from "@/views/layout/Layout.vue";
|
||||
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
|
||||
import {createRouter, createWebHashHistory, RouteRecordRaw} from "vue-router";
|
||||
|
||||
import NProgress from "nprogress";
|
||||
import "nprogress/nprogress.css";
|
||||
import Common from "@/common/Common";
|
||||
|
||||
NProgress.configure({
|
||||
easing: 'ease',
|
||||
@ -25,6 +26,9 @@ const routes: Array<RouteRecordRaw> = [
|
||||
name: "dashboard",
|
||||
redirect: "/dashboard/index",
|
||||
component: LayoutContainer,
|
||||
meta: {
|
||||
requireAuth: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "index",
|
||||
@ -37,6 +41,9 @@ const routes: Array<RouteRecordRaw> = [
|
||||
name: "console",
|
||||
redirect: "/console/index",
|
||||
component: LayoutContainer,
|
||||
meta: {
|
||||
requireAuth: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "index",
|
||||
@ -48,6 +55,9 @@ const routes: Array<RouteRecordRaw> = [
|
||||
path: "/admin",
|
||||
name: "admin",
|
||||
component: LayoutContainer,
|
||||
meta: {
|
||||
requireAuth: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "source",
|
||||
@ -69,6 +79,22 @@ const routes: Array<RouteRecordRaw> = [
|
||||
component: () => import("../views/common/NotFound.vue")
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: "/auth",
|
||||
name: "auth",
|
||||
children: [
|
||||
{
|
||||
name: "signin",
|
||||
path: "signin",
|
||||
component: () => import("../views/pages/auth/AuthSignin.vue")
|
||||
},
|
||||
{
|
||||
name: "signup",
|
||||
path: "signup",
|
||||
component: () => import("../views/pages/auth/AuthSignup.vue")
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@ -77,13 +103,30 @@ const router = createRouter({
|
||||
routes
|
||||
});
|
||||
|
||||
const authRouterWith = '/auth/sign';
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.start();
|
||||
if (to.matched.length === 0) {
|
||||
next({ name: "routerNotFound" })
|
||||
next({name: "routerNotFound"})
|
||||
}
|
||||
else {
|
||||
next();
|
||||
if (to.meta.requireAuth) {
|
||||
if (localStorage.getItem(Common.token)) {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
next('/auth/signin');
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (localStorage.getItem(Common.token) && to.path.startsWith(authRouterWith)) {
|
||||
next('/');
|
||||
}
|
||||
else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
18
web/console-fe/src/services/AuthService.ts
Normal file
18
web/console-fe/src/services/AuthService.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import {ResponseModel} from "@/model/ResponseModel";
|
||||
import {HttpCommon} from "@/common/HttpCommon";
|
||||
import {AuthUser} from "@/model/AuthUser";
|
||||
|
||||
const defaultAuth = "/api/auth";
|
||||
|
||||
export class AuthService
|
||||
{
|
||||
signin(configure: AuthUser): Promise<ResponseModel>
|
||||
{
|
||||
return new HttpCommon().post(defaultAuth + '/signin', configure);
|
||||
}
|
||||
|
||||
signup(configure: AuthUser): Promise<ResponseModel>
|
||||
{
|
||||
return new HttpCommon().post(defaultAuth + '/signup', configure);
|
||||
}
|
||||
}
|
@ -1,62 +1,100 @@
|
||||
<template>
|
||||
<a-menu theme="dark" mode="horizontal" :style="{ lineHeight: '64px' }">
|
||||
<a-menu-item>DataCap(incubator)</a-menu-item>
|
||||
<a-menu-item key="dashboard">
|
||||
<router-link to="/">
|
||||
<home-filled/>
|
||||
{{ $t('common.home') }}
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="console">
|
||||
<router-link to="/console/index">
|
||||
<console-sql-outlined/>
|
||||
{{ $t('common.query') }}
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
<a-sub-menu key="admin">
|
||||
<template #icon>
|
||||
<setting-outlined/>
|
||||
</template>
|
||||
<template #title>{{ $t('common.admin') }}</template>
|
||||
<a-menu-item key="admin_source">
|
||||
<router-link to="/admin/source">
|
||||
<aim-outlined/>
|
||||
{{ $t('common.source') }}
|
||||
<div :style="{ clear: 'both' }">
|
||||
<a-menu theme="dark" mode="horizontal" :style="{lineHeight: '64px', float: 'left', width: '60%'}">
|
||||
<a-menu-item>DataCap(incubator)</a-menu-item>
|
||||
<a-menu-item key="dashboard">
|
||||
<router-link to="/">
|
||||
<home-filled/>
|
||||
{{ $t('common.home') }}
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="admin_history">
|
||||
<router-link to="/admin/history">
|
||||
<history-outlined/>
|
||||
{{ $t('common.history') }}
|
||||
<a-menu-item key="console">
|
||||
<router-link to="/console/index">
|
||||
<console-sql-outlined/>
|
||||
{{ $t('common.query') }}
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
</a-sub-menu>
|
||||
<a-sub-menu key="language">
|
||||
<template #icon>
|
||||
<a-dropdown placement="bottom">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="en" @click="handlerChangeLang('en')">
|
||||
English
|
||||
</a-menu-item>
|
||||
<a-menu-item key="zhCN" @click="handlerChangeLang('zh_cn')">
|
||||
中文
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<translation-outlined/>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
</a-sub-menu>
|
||||
</a-menu>
|
||||
<a-sub-menu key="admin">
|
||||
<template #icon>
|
||||
<setting-outlined/>
|
||||
</template>
|
||||
<template #title>{{ $t('common.admin') }}</template>
|
||||
<a-menu-item key="admin_source">
|
||||
<router-link to="/admin/source">
|
||||
<aim-outlined/>
|
||||
{{ $t('common.source') }}
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="admin_history">
|
||||
<router-link to="/admin/history">
|
||||
<history-outlined/>
|
||||
{{ $t('common.history') }}
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
</a-sub-menu>
|
||||
</a-menu>
|
||||
<a-menu theme="dark" mode="horizontal" :style="{lineHeight: '64px', float: 'right'}">
|
||||
<a-sub-menu key="language">
|
||||
<template #icon>
|
||||
<a-dropdown placement="bottom">
|
||||
<template #overlay>
|
||||
<a-menu style="margin-top: 5px;">
|
||||
<a-menu-item key="en" @click="handlerChangeLang('en')">
|
||||
{{ $t('common.english') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="zhCN" @click="handlerChangeLang('zh_cn')">
|
||||
{{ $t('common.chinese') }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<translation-outlined/>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
</a-sub-menu>
|
||||
<a-sub-menu>
|
||||
<template #icon>
|
||||
<a-dropdown placement="bottomRight">
|
||||
<template #overlay>
|
||||
<a-menu style="margin-top: 5px;">
|
||||
<a-menu-item @click="handlerLogout">
|
||||
{{ $t('common.logout') }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-avatar style="background-color: #87d068">{{ username }}</a-avatar>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
</a-sub-menu>
|
||||
</a-menu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent} from "vue";
|
||||
import {AimOutlined, HomeFilled, SettingOutlined} from '@ant-design/icons-vue';
|
||||
import Common from "@/common/Common";
|
||||
import {AuthResponse} from "@/model/AuthResponse";
|
||||
import router from "@/router";
|
||||
|
||||
export default defineComponent({
|
||||
name: "LayoutHeader",
|
||||
setup()
|
||||
{
|
||||
let username;
|
||||
const authUser = JSON.parse(localStorage.getItem(Common.token) || '{}') as AuthResponse;
|
||||
if (authUser) {
|
||||
username = authUser.username;
|
||||
}
|
||||
|
||||
const handlerLogout = () => {
|
||||
localStorage.removeItem(Common.token);
|
||||
router.push('/auth/signin')
|
||||
}
|
||||
return {
|
||||
username,
|
||||
handlerLogout
|
||||
}
|
||||
},
|
||||
components: {HomeFilled, SettingOutlined, AimOutlined},
|
||||
computed: {},
|
||||
methods: {
|
||||
|
82
web/console-fe/src/views/pages/auth/AuthSignin.vue
Normal file
82
web/console-fe/src/views/pages/auth/AuthSignin.vue
Normal file
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
<a-row :gutter="[8,8]">
|
||||
<a-col :span="8"/>
|
||||
<a-col :span="8">
|
||||
<a-result :title="'DataCap ' + $t('common.login')">
|
||||
<template #icon>
|
||||
<smile-twoTone/>
|
||||
</template>
|
||||
<template #extra>
|
||||
<a-alert v-if="formState.message" :message="formState.message" type="error" show-icon style="margin-bottom: 10px;"/>
|
||||
<a-form :model="formState" name="basic" :label-col="{ span: 6 }"
|
||||
:wrapper-col="{ span: 16 }" @finish="handlerAuthSignin">
|
||||
<a-form-item :label="$t('common.username')" name="username"
|
||||
:rules="[{ required: true, message: $t('required.username') }]">
|
||||
<a-input v-model:value="formState.username"/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('common.password')" name="password"
|
||||
:rules="[{ required: true, message: $t('required.password') }]">
|
||||
<a-input-password v-model:value="formState.password"/>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{ offset: 6, span: 16 }">
|
||||
<a-button :disabled="disabled" :loading="formState.loading" type="primary" html-type="submit">{{ $t('common.login') }}</a-button>
|
||||
<a-button type="dashed" style="margin-left: 10px;" @click="handlerGoSignup">{{ $t('common.register') }}</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
</a-result>
|
||||
</a-col>
|
||||
<a-col :span="8"/>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {computed, defineComponent, reactive} from 'vue';
|
||||
import {AuthUser} from "@/model/AuthUser";
|
||||
import {AuthService} from "@/services/AuthService";
|
||||
import Common from "@/common/Common";
|
||||
import router from "@/router";
|
||||
|
||||
export default defineComponent({
|
||||
setup()
|
||||
{
|
||||
const formState = reactive<AuthUser>({
|
||||
username: '',
|
||||
password: ''
|
||||
});
|
||||
|
||||
const disabled = computed(() => {
|
||||
return !(formState.username && formState.password);
|
||||
});
|
||||
|
||||
const handlerAuthSignin = (values: any) => {
|
||||
formState.loading = true;
|
||||
new AuthService().signin(values)
|
||||
.then(response => {
|
||||
if (response.status) {
|
||||
localStorage.setItem(Common.token, JSON.stringify(response.data));
|
||||
router.push('/');
|
||||
}
|
||||
else {
|
||||
formState.message = response.message;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
formState.loading = undefined;
|
||||
});
|
||||
};
|
||||
|
||||
const handlerGoSignup = () => {
|
||||
router.push('/auth/signup');
|
||||
}
|
||||
return {
|
||||
formState,
|
||||
disabled,
|
||||
handlerAuthSignin,
|
||||
handlerGoSignup
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
83
web/console-fe/src/views/pages/auth/AuthSignup.vue
Normal file
83
web/console-fe/src/views/pages/auth/AuthSignup.vue
Normal file
@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
<a-row :gutter="[8,8]">
|
||||
<a-col :span="8"/>
|
||||
<a-col :span="8">
|
||||
<a-result :title="'DataCap ' + $t('common.register')">
|
||||
<template #icon>
|
||||
<smile-twoTone/>
|
||||
</template>
|
||||
<template #extra>
|
||||
<a-alert v-if="formState.message" :message="formState.message" type="error" show-icon style="margin-bottom: 10px;"/>
|
||||
<a-form :model="formState" name="basic" :label-col="{ span: 6 }"
|
||||
:wrapper-col="{ span: 16 }" @finish="handlerAuthSignup">
|
||||
<a-form-item :label="$t('common.username')" name="username"
|
||||
:rules="[{ required: true, message: $t('required.username') }]">
|
||||
<a-input v-model:value="formState.username"/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('common.password')" name="password"
|
||||
:rules="[{ required: true, message: $t('required.password') }]">
|
||||
<a-input-password v-model:value="formState.password"/>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{ offset: 6, span: 16 }">
|
||||
<a-button :disabled="disabled" :loading="formState.loading" type="primary" html-type="submit">{{ $t('common.register') }}</a-button>
|
||||
<a-button type="dashed" style="margin-left: 10px;" @click="handlerGoSignin">{{ $t('common.login') }}</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
</a-result>
|
||||
</a-col>
|
||||
<a-col :span="8"/>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {computed, defineComponent, reactive} from 'vue';
|
||||
import {AuthUser} from "@/model/AuthUser";
|
||||
import {AuthService} from "@/services/AuthService";
|
||||
import Common from "@/common/Common";
|
||||
import router from "@/router";
|
||||
import {message} from "ant-design-vue";
|
||||
|
||||
export default defineComponent({
|
||||
setup()
|
||||
{
|
||||
const formState = reactive<AuthUser>({
|
||||
username: '',
|
||||
password: ''
|
||||
});
|
||||
|
||||
const disabled = computed(() => {
|
||||
return !(formState.username && formState.password);
|
||||
});
|
||||
|
||||
const handlerAuthSignup = (values: any) => {
|
||||
formState.loading = true;
|
||||
new AuthService().signup(values)
|
||||
.then(response => {
|
||||
if (response.status) {
|
||||
message.success('Success');
|
||||
router.push('/auth/signin');
|
||||
}
|
||||
else {
|
||||
formState.message = response.message;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
formState.loading = undefined;
|
||||
});
|
||||
};
|
||||
|
||||
const handlerGoSignin = () => {
|
||||
router.push('/auth/signin');
|
||||
}
|
||||
return {
|
||||
formState,
|
||||
disabled,
|
||||
handlerAuthSignup,
|
||||
handlerGoSignin
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user