搭建一个简单的前后端工程, 前端vue-element-admin ,后端 springboot,数据库postgresql 使用docker打包发布. 功能方面预置登录,账号功能. 方便后续自己添加业务逻辑实现各种管理系统
前端
部署脚本
git fetch --all
git reset --hard origin/dev-prod
npm install
npm run build:prod
chown 1000:1000 -R perftest
echo "start perfTest-vue success!"
echo "restart nginx"
docker restart nginx
echo "restart nginx success"
vue.config.js
devServer: {
port: port,
open: true,
overlay: {
warnings: false,
errors: true
},
// before: require('./mock/mock-server.js'),
proxy: {
'/dev-api': {
// 目标 API 地址
target: 'http://localhost:8848', // 开发环境
ws: true,
changeOrigin: true,
pathRewrite: {
'^/dev-api': '/dev-api' // 这里理解成用‘/api’代替target里面的地址,后面组件中我们掉接口时直接用api代替 比如我要调用'http://40.00.100.100:3002/user/add',直接写‘/api/user/add’即可
}
}
}
},
后端
部署脚本
git fetch --all
git reset --hard origin/dev_prod
mvn clean package -DskipTests
cp -rf config/ target/
mv target/test-0.0.1-SNAPSHOT.jar target/test.jar
pid=`ps -ef | grep automate_test_pord.jar | grep -v grep | awk '{print $2}'`
kill -9 $pid
echo "pid $pid killed"
nohup java -jar ./target/automate_test_pord.jar > log_java.log 2>&1 &
echo "star success!"
后端dockerfile
FROM maven:3.8.1-jdk-8 AS MAVEN_BUILD
RUN mkdir -p /build/
COPY src /build/src/
COPY pom.xml /build/
COPY scripts/simpod_docker/settings.xml /etc/maven/setting.xml
WORKDIR /build/
RUN echo "7.223.199.227 mirrors.tools.huawei.com" >> /etc/hosts \
&& mvn clean package --settings /etc/maven/setting.xml
#基于java8
FROM java:8
ENV TZ=Asia/Shanghai
#修改时区
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo 'Asia/Shanghai' >/etc/timezone
#创建一个目录存放jar包
RUN mkdir -p /opt/openstack/
COPY --from=MAVEN_BUILD /build/target/openstack-2.0.jar /opt/openstack/
#添加进入docker容器后的目录
WORKDIR /opt/openstack
#对外暴露的端口号
EXPOSE 8848 20006
#运行脚本,启动springboot项目,这里我们指定加载配置文件的位置,并且通过数据卷挂载同步到容器中
ENTRYPOINT ["java", "-jar", "openstack-2.0.jar", "-Xms4g", "-Xmx4g", "--spring.profiles.active=simpod", "-Dspring.config.location=/opt/openstack/config/application-simpod.yml"]
nginx dockerfile
FROM nginx:stable
ENV TZ=Asia/Shanghai
ENV NGINX_PORT=80
COPY default.conf /etc/nginx/conf.d/default.conf
nginx config
client_max_body_size 1g;
server {
listen 80;
root /usr/share/nginx/www;
index index.html index.htm;
rewrite ^/code /config;
location / {
proxy_pass http://grafana:3000/;
}
location /perftest/ {
alias /var/lib/perf_autotest_vue/perftest/;
index index.html;
try_files $uri $uri/ /perftest/index.html;
}
location /prod-api/ {
proxy_read_timeout 3600; # 秒
proxy_pass http://ip:port/prod-api/;
}
}
后端
common/handle/AuthSuccessHandler
package com.example.demo.handler;
import com.example.demo.common.R;
import com.example.demo.mapper.UsrMapper;
import com.example.demo.model.UsrVo;
import com.example.demo.util.JwtUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
@Component
@RequiredArgsConstructor
public class AuthSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private final UsrMapper usrMapper;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws
IOException {
String id = (String) authentication.getPrincipal();
UsrVo usrVo = new UsrVo(usrMapper.selectById(id));
String token = JwtUtil.createToken(usrVo.getId(), usrVo.getRoles());
usrVo.setToken(token);
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write(OBJECT_MAPPER.writeValueAsString(R.ok(usrVo)));
writer.flush();
writer.close();
}}
common/handle/AuthFailHandler
package com.example.demo.handler;
import com.example.demo.common.R;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class AuthFailHandler implements AuthenticationFailureHandler {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws
IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write(OBJECT_MAPPER.writeValueAsString(R.failed("用户名或密码认证失败,请重试")));
writer.flush();
writer.close();
}}
filter/tokenFilter
package com.example.demo.filter;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.demo.common.R;
import com.example.demo.util.JwtUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class TokenFilter extends OncePerRequestFilter {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws
ServletException,
IOException {
String token = request.getHeader("X-Token");
if (StringUtils.isNotBlank(token)) {
try {
DecodedJWT jwt = JwtUtil.parseToken(token);
String userId = jwt.getClaim("userId").asString();
List<String> roles = jwt.getClaim("roles").asList(String.class);
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
} if (null == SecurityContextHolder.getContext().getAuthentication()) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
userId, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
} } catch (Exception e) {
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write(OBJECT_MAPPER.writeValueAsString(R.failed("token校验失败,请重试")));
writer.flush();
writer.close();
return;
} } chain.doFilter(request, response);
}}
config/CorsConfig 跨域处理
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")//项目中的所有接口都支持跨域
.allowedOriginPatterns("*")//所有地址都可以访问,也可以配置具体地址
.allowCredentials(true)
.allowedMethods("*")//"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"
.maxAge(3600);// 跨域允许时间
}
}
config/SecurityConfiguration
import com.example.demo.filter.TokenFilter;
import com.example.demo.handler.AuthFailHandler;
import com.example.demo.handler.AuthSuccessHandler;
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.web.authentication.UsernamePasswordAuthenticationFilter;
import lombok.RequiredArgsConstructor;
/**
* Author: wxb * Project: spring_security_example * Create Date: 2018/10/18 * Create Time: 17:36 * Description: Security 配置类
*/
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final UserDetailsProvider userDetailsProvider;
private final AuthSuccessHandler authSuccessHandler;
private final AuthFailHandler authFailHandler;
private final TokenFilter tokenFilter;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.userDetailsService(myUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
// 基于数据库验证
auth.authenticationProvider(userDetailsProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").permitAll() // 开发时候不做权限校验
.antMatchers("/user/*", "/login", "/index", "/static", "/error","/alarm/*").permitAll()
.antMatchers("/file/download/*").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
// .loginPage("/login")
// .defaultSuccessUrl("/jenkins_job")
.usernameParameter("username").passwordParameter("password")
.successHandler(authSuccessHandler)
.failureHandler(authFailHandler)
.and()
.csrf().disable()
.logout().disable();
http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
}}
config/UserDetailProvider用户处理类
package com.example.demo.config;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.demo.mapper.UsrMapper;
import com.example.demo.model.Usr;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
@RequiredArgsConstructor
public class UserDetailsProvider implements AuthenticationProvider {
private final UsrMapper usrMapper;
@Override
public Authentication authenticate(Authentication authentication) {
log.info("数据库验证开始");
String username = authentication.getName();
String password = authentication.getCredentials().toString();
LambdaQueryWrapper<Usr> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Usr::getUsername, username);
Usr usr = usrMapper.selectOne(wrapper);
if (usr == null) {
return null;
} BCryptPasswordEncoder bCryptEncoder = new BCryptPasswordEncoder();
if (bCryptEncoder.matches(password, usr.getPassword())) {
log.info("数据库验证通过");
return new UsernamePasswordAuthenticationToken(
usr.getId(), password, usr.getAuthorities());
} else {
log.info("密码错误,转ldap验证");
} return null;
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(
UsernamePasswordAuthenticationToken.class);
}}
jwtutil token工具类
package com.example.demo.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
public class JwtUtil {
public static final String SECRET = "PERF_CLOUD_TEST";
public static String createToken(String id, List<String> roles) {
Algorithm algorithm = Algorithm.HMAC256(SECRET);
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.HOUR, 12);
Date time = calendar.getTime();
return JWT.create()
.withIssuer("auth0")
.withExpiresAt(time)
.withClaim("userId", id)
.withClaim("roles", roles)
.sign(algorithm);
}
public static void verifyToken(String token) {
Algorithm algorithm = Algorithm.HMAC256(SECRET);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("auth0")
.build();
verifier.verify(token);
}
public static DecodedJWT parseToken(String token) {
Algorithm algorithm = Algorithm.HMAC256(SECRET);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("auth0")
.build();
return verifier.verify(token);
}
}
usrvo类
package com.example.demo.model;
import java.util.Arrays;
import java.util.List;
import lombok.Data;
@Data
public class UsrVo {
String id;
String userName;
// jwt token
String token;
List<String> roles;
public UsrVo(Usr usr) {
this.id = usr.getId();
this.userName = usr.getUsername();
this.roles = Arrays.asList(usr.getRoles().split(","));
}
public UsrVo(Usr usr, String token) {
this.id = usr.getId();
this.userName = usr.getUsername();
this.roles = Arrays.asList(usr.getRoles().split(","));
this.token = token;
}}
usr类
package com.example.demo.model;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import lombok.Data;
@Data
public class Usr implements UserDetails {
@TableId()
String id;
String userName;
String cnName;
String fullName;
@JsonIgnore
String password;
// 角色 // @JsonIgnore
String roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
String[] authorities = roles.split(",");
List<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>();
for (String role : authorities) {
simpleGrantedAuthorities.add(new SimpleGrantedAuthority(role));
} return simpleGrantedAuthorities;
}
@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;
}}
usrmapper
@Mapper
package com.example.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.model.Usr;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UsrMapper extends BaseMapper<Usr> {
}
通用response
package com.example.demo.common;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class R<T> implements Serializable {
private static final long serialVersionUID = 1L;
private int code;
private String msg;
private T data;
private static Integer SUCCESS = 0;
private static Integer FAIL = 1;
public static <T> R<T> ok() {
return restResult(null, SUCCESS, null);
}
public static <T> R<T> ok(T data) {
return restResult(data, SUCCESS, null);
}
public static <T> R<T> ok(T data, String msg) {
return restResult(data, SUCCESS, msg);
}
public static <T> R<T> failed() {
return restResult(null, FAIL, null);
}
public static <T> R<T> failed(String msg) {
return restResult(null, FAIL, msg);
}
public static <T> R<T> failed(T data) {
return restResult(data, FAIL, null);
}
public static <T> R<T> failed(T data, String msg) {
return restResult(data, FAIL, msg);
}
private static <T> R<T> restResult(T data, int code, String msg) {
R<T> apiResult = new R<>();
apiResult.setCode(code);
apiResult.setData(data);
apiResult.setMsg(msg);
return apiResult;
}}
application.yml
spring:
datasource:
url: jdbc:postgresql://xxx.xxx.xxx.xxx:xxx/postgres
username: username
password: password
driver-class-name: org.postgresql.Driver
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.80</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.2.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>