谷粒学院

Spring Security整合

一、Spring Security介绍

1、框架介绍

Spring 是一个非常流行和成功的 Java 应用开发框架。Spring Security 基于 Spring 框架,提供了一套Web 应用安全性的完整解决方案。

一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分。
(1)用户认证指的是:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。说人话就是登录认证
(2)用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。判断是普通用户还是管理员基于功能访问权限

Spring Security其实就是用filter,多请求的路径进行过滤。
(1)如果是基于Session,那么Spring-security会对cookie里的sessionid进行解析,找到服务器存储的sesion信息,然后判断当前用户是否符合请求的要求。
(2)如果是token,则是解析出token,然后将当前请求加入到Spring-security管理的权限信息中去

2、认证与授权实现思路

如果系统的模块众多,每个模块都需要就行授权与认证,所以我们选择基于token的形式进行授权与认证,用户根据用户名密码认证成功,然后获取当前用户角色的一系列权限值,并以用户名为key,权限列表为value的形式存入redis缓存中,根据用户名相关信息生成token返回,浏览器将token记录到cookie中,每次调用api接口都默认将token携带到header请求头中,Spring-security解析header头获取token信息,解析token获取当前用户名,根据用户名就可以从redis中获取权限列表,这样Spring-security就能够判断当前请求是否有权限访问

1、用户名密码认证成功,获取用户的权限值;并以用户名为Key,权限列表为Value的形式存储在Redis缓存中

2、根据用户名相关信息返回token,浏览器将token记录到cookie中,没请求都会在请求头中携带token

3、请求中,Spring-security会解析请求头中的token,并获取用户名;

4、根据用户名为key,去redis中获取value值权限列表

5、根据权限列表来判断用户是否有权限访问


二、整合Spring Security

1、在common下创建spring_security模块

image-20210310212148861

2、在spring_security引入相关依赖

<dependencies>
    
    <dependency>
        <groupId>com.achang</groupId>
        <artifactId>common-utils</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
    
    <!-- Spring Security依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
    </dependency>
    
</dependencies>

3、在service_acl引入上面spring_security的依赖

<dependency>
    <groupId>com.atguigu</groupId>
    <artifactId>spring_security</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

image-20210310212348586

代码结构说明:

security对应着:认证授权,所有下面filter对应两个过滤器

image-20210310212510840

4、创建spring security核心配置类

Spring Security的核心配置就是继承WebSecurityConfigurerAdapter并注解@EnableWebSecurity的配置。
这个配置指明了用户名密码的处理方式、请求路径的开合、登录登出控制等和安全相关的配置

package com.achang.serurity.config;

import com.achang.serurity.filter.TokenAuthenticationFilter;
import com.achang.serurity.filter.TokenLoginFilter;
import com.achang.serurity.security.DefaultPasswordEncoder;
import com.achang.serurity.security.TokenLogoutHandler;
import com.achang.serurity.security.TokenManager;
import com.achang.serurity.security.UnauthorizedEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;

/**
 * <p>
 * Security配置类
 * </p>
 *
 * @author qy
 * @since 2019-11-18
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {

    private UserDetailsService userDetailsService; //自己写的查询数据库类
    private TokenManager tokenManager;//token生成工具类
    private DefaultPasswordEncoder defaultPasswordEncoder;//密码处理
    private RedisTemplate redisTemplate;//操作redis

    @Autowired
    public TokenWebSecurityConfig(UserDetailsService userDetailsService, DefaultPasswordEncoder defaultPasswordEncoder,
                                  TokenManager tokenManager, RedisTemplate redisTemplate) {
        this.userDetailsService = userDetailsService;
        this.defaultPasswordEncoder = defaultPasswordEncoder;
        this.tokenManager = tokenManager;
        this.redisTemplate = redisTemplate;
    }

    /**
     * 核心配置设置
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.exceptionHandling()
                .authenticationEntryPoint(new UnauthorizedEntryPoint())
                .and().csrf().disable()
                .authorizeRequests()
                .anyRequest().authenticated()
                .and().logout().logoutUrl("/admin/acl/index/logout")//设置退出地址
                .addLogoutHandler(new TokenLogoutHandler(tokenManager,redisTemplate)).and()
                .addFilter(new TokenLoginFilter(authenticationManager(), tokenManager, redisTemplate))
                .addFilter(new TokenAuthenticationFilter(authenticationManager(), tokenManager, redisTemplate)).httpBasic();
    }

    /**
     * 密码处理
     * @param auth
     * @throws Exception
     */
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(defaultPasswordEncoder);
    }

    /**
     * 配置哪些请求不拦截
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/api/**",
                "/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**"
               );//设置哪些路径不做拦截,如swagger等
        web.ignoring().antMatchers("/*/**"
        );
    }
}

5、创建认证授权相关的工具类

image-20210310215221943

(1)DefaultPasswordEncoder:密码处理的方法

package com.achang.serurity.security;

import com.achang.commonutils.MD5;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

/**
 * <p>
 * t密码的处理方法类型
 * </p>
 *
 * @author qy
 * @since 2019-11-08
 */
@Component
public class DefaultPasswordEncoder implements PasswordEncoder {

    public DefaultPasswordEncoder() {
        this(-1);
    }

    /**
     * @param strength
     *            the log rounds to use, between 4 and 31
     */
    public DefaultPasswordEncoder(int strength) {

    }

    public String encode(CharSequence rawPassword) {
        return MD5.encrypt(rawPassword.toString());
    }

    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return encodedPassword.equals(MD5.encrypt(rawPassword.toString()));
    }
}

(2) TokenManager:token操作的工具类

package com.achang.serurity.security;

import io.jsonwebtoken.CompressionCodecs;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * <p>
 * token管理
 * </p>
 *
 * @author qy
 * @since 2019-11-08
 */
@Component
public class TokenManager {

    private long tokenExpiration = 24*60*60*1000;
    private String tokenSignKey = "123456";

    public String createToken(String username) {
        String token = Jwts.builder().setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
                .signWith(SignatureAlgorithm.HS512, tokenSignKey).compressWith(CompressionCodecs.GZIP).compact();
        return token;
    }

    public String getUserFromToken(String token) {
        String user = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token).getBody().getSubject();
        return user;
    }

    public void removeToken(String token) {
        //jwttoken无需删除,客户端扔掉即可。
    }

}

(3) TokenLogoutHandler:退出实现

package com.achang.serurity.security;

import com.achang.commonutils.R;
import com.achang.commonutils.ResponseUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * <p>
 * 登出业务逻辑类
 * </p>
 *
 * @author qy
 * @since 2019-11-08
 */
public class TokenLogoutHandler implements LogoutHandler {

    private TokenManager tokenManager;
    private RedisTemplate redisTemplate;

    public TokenLogoutHandler(TokenManager tokenManager, RedisTemplate redisTemplate) {
        this.tokenManager = tokenManager;
        this.redisTemplate = redisTemplate;
    }

    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
        String token = request.getHeader("token");
        if (token != null) {
            tokenManager.removeToken(token);

            //清空当前用户缓存中的权限数据
            String userName = tokenManager.getUserFromToken(token);
            redisTemplate.delete(userName);
        }
        ResponseUtil.out(response, R.ok());
    }

}

(4) UnauthorizedEntryPoint:未授权统一处理

如果没有权限,该怎么处理

package com.achang.serurity.security;

import com.achang.commonutils.R;
import com.achang.commonutils.ResponseUtil;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

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

/**
 * <p>
 * 未授权的统一处理方式
 * </p>
 *
 * @author qy
 * @since 2019-11-08
 */
public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException) throws IOException, ServletException {

        ResponseUtil.out(response, R.error());
    }
}


6、创建认证授权实体类

image-20210310220134258

(1)SecutityUser

package com.achang.serurity.entity;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * <p>
 * 安全认证用户详情信息
 * </p>
 *
 * @author qy
 * @since 2019-11-08
 */
@Data
@Slf4j
public class SecurityUser implements UserDetails {

    //当前登录用户
    private transient User currentUserInfo;

    //当前权限
    private List<String> permissionValueList;

    public SecurityUser() {
    }

    public SecurityUser(User user) {
        if (user != null) {
            this.currentUserInfo = user;
        }
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        for(String permissionValue : permissionValueList) {
            if(StringUtils.isEmpty(permissionValue)) continue;
            SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
            authorities.add(authority);
        }

        return authorities;
    }

    @Override
    public String getPassword() {
        return currentUserInfo.getPassword();
    }

    @Override
    public String getUsername() {
        return currentUserInfo.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

(2)User

package com.achang.serurity.entity;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;

/**
 * <p>
 * 用户实体类
 * </p>
 *
 * @author qy
 * @since 2019-11-08
 */
@Data
@ApiModel(description = "用户实体类")
public class User implements Serializable {

	private static final long serialVersionUID = 1L;

	@ApiModelProperty(value = "微信openid")
	private String username;

	@ApiModelProperty(value = "密码")
	private String password;

	@ApiModelProperty(value = "昵称")
	private String nickName;

	@ApiModelProperty(value = "用户头像")
	private String salt;

	@ApiModelProperty(value = "用户签名")
	private String token;

}

7、创建认证和授权的核心filter

security的认证和授权过滤器

image-20210310220218213

(1)TokenLoginFilter:认证的filter

package com.achang.serurity.filter;

import com.achang.commonutils.R;
import com.achang.commonutils.ResponseUtil;
import com.achang.serurity.entity.SecurityUser;
import com.achang.serurity.entity.User;
import com.achang.serurity.security.TokenManager;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;

/**
 * <p>
 * 登录过滤器,继承UsernamePasswordAuthenticationFilter,对用户名密码进行登录校验
 * </p>
 *
 * @author qy
 * @since 2019-11-08
 */
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;
    private TokenManager tokenManager;
    private RedisTemplate redisTemplate;

    public TokenLoginFilter(AuthenticationManager authenticationManager, TokenManager tokenManager, RedisTemplate redisTemplate) {
        this.authenticationManager = authenticationManager;
        this.tokenManager = tokenManager;
        this.redisTemplate = redisTemplate;
        this.setPostOnly(false);
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/acl/login","POST"));
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)
            throws AuthenticationException {
        try {
            User user = new ObjectMapper().readValue(req.getInputStream(), User.class);

            return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), new ArrayList<>()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 登录成功
     * @param req
     * @param res
     * @param chain
     * @param auth
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain,
                                            Authentication auth) throws IOException, ServletException {
        SecurityUser user = (SecurityUser) auth.getPrincipal();
        String token = tokenManager.createToken(user.getCurrentUserInfo().getUsername());
        redisTemplate.opsForValue().set(user.getCurrentUserInfo().getUsername(), user.getPermissionValueList());

        ResponseUtil.out(res, R.ok().data("token", token));
    }

    /**
     * 登录失败
     * @param request
     * @param response
     * @param e
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException e) throws IOException, ServletException {
        ResponseUtil.out(response, R.error());
    }
}

(2)TokenAuthenticationFilter:

package com.achang.serurity.filter;

import com.achang.commonutils.R;
import com.achang.commonutils.ResponseUtil;
import com.achang.serurity.security.TokenManager;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * <p>
 * 访问过滤器
 * </p>
 *
 * @author qy
 * @since 2019-11-08
 */
public class TokenAuthenticationFilter extends BasicAuthenticationFilter {
    private TokenManager tokenManager;
    private RedisTemplate redisTemplate;

    public TokenAuthenticationFilter(AuthenticationManager authManager, TokenManager tokenManager,RedisTemplate redisTemplate) {
        super(authManager);
        this.tokenManager = tokenManager;
        this.redisTemplate = redisTemplate;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        logger.info("================="+req.getRequestURI());
        if(req.getRequestURI().indexOf("admin") == -1) {
            chain.doFilter(req, res);
            return;
        }

        UsernamePasswordAuthenticationToken authentication = null;
        try {
            authentication = getAuthentication(req);
        } catch (Exception e) {
            ResponseUtil.out(res, R.error());
        }

        if (authentication != null) {
            SecurityContextHolder.getContext().setAuthentication(authentication);
        } else {
            ResponseUtil.out(res, R.error());
        }
        chain.doFilter(req, res);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        // token置于header里
        String token = request.getHeader("token");
        if (token != null && !"".equals(token.trim())) {
            String userName = tokenManager.getUserFromToken(token);

            List<String> permissionValueList = (List<String>) redisTemplate.opsForValue().get(userName);
            Collection<GrantedAuthority> authorities = new ArrayList<>();
            for(String permissionValue : permissionValueList) {
                if(StringUtils.isEmpty(permissionValue)) continue;
                SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
                authorities.add(authority);
            }

            if (!StringUtils.isEmpty(userName)) {
                return new UsernamePasswordAuthenticationToken(userName, token, authorities);
            }
            return null;
        }
        return null;
    }
}

创建查询用户类和前端对接

一 、创建自定义查询用户类

(1)在service_acl模块创建,因为其他模板不会用到

image-20210310222220994

  • UserDetailsServiceImpl

创建查询登录和用户权限的类,写的位置随意,要求实现UserDetailsService

下面的对象为具体项目中的用户账户密码的那个实体类对象,用于下面的整合,查找数据库的内容

package com.achang.aclservice.service.impl;

import com.achang.aclservice.entity.User;
import com.achang.aclservice.service.PermissionService;
import com.achang.aclservice.service.UserService;
import com.achang.serurity.entity.SecurityUser;
import org.springframework.beans.BeanUtils;
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;

import java.util.List;


/**
 * <p>
 * 自定义userDetailsService - 认证用户详情
 * </p>
 *
 * @author qy
 * @since 2019-11-08
 */
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserService userService;//操作对应user表的数据库service对象

    @Autowired
    private PermissionService permissionService;

    /***
     * 根据账号获取用户信息
     * @param username:
     * @return: org.springframework.security.core.userdetails.UserDetails
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 从数据库中取出用户信息
        User user = userService.selectByUsername(username);

        // 判断用户是否存在
        if (null == user){
            //throw new UsernameNotFoundException("用户名不存在!");
        }
        // 返回UserDetails实现类
        com.achang.serurity.entity.User curUser = new com.achang.serurity.entity.User();
        BeanUtils.copyProperties(user,curUser);

        List<String> authorities = permissionService.selectPermissionValueByUserId(user.getId());
        SecurityUser securityUser = new SecurityUser(curUser);
        securityUser.setPermissionValueList(authorities);
        return securityUser;
    }

}

image-20210310222457476


二、后端接口和前端页面对接

1、在前端项目中下载依赖

npm install --save vuex-persistedstate

2、在node_modules文件夹中替换element-ui依赖

这里替换之前的element-ui的组件,老师后面赶工太严重了,全都是替换

删除掉项目里的element-ui,然后将新的复制进来

image-20210311102931525

3、替换相关文件

这里阿昌直接就看了,没对着做了,直接全部替换代码,太过分了

image-20210311111540281


Nacos配置中心

一、配置中心介绍

image-20210311112242724

1、Spring Cloud Config

Spring Cloud Config 为分布式系统的外部配置提供了服务端和客户端的支持方案。在配置的服务端您可以在所有环境中为应用程序管理外部属性的中心位置。客户端和服务端概念上的Spring Environment和 PropertySource 抽象保持同步, 它们非常适合Spring应用程序,但是可以与任何语言中运行的应用程序一起使用。当应用程序在部署管道中从一个开发到测试直至进入生产时,您可以管理这些环境之间的配置,并确保应用程序在迁移时具有它们需要运行的所有内容。服务器存储后端的默认实现使用git,因此它很容易支持标记版本的配置环境,并且能够被管理内容的各种工具访问。很容易添加替代的实现,并用Spring配置将它们插入。

Spring Cloud Config 包含了Client和Server两个部分,server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,client通过接口获取数据、并依据此数据初始化自己的应用。Springcloud使用git或svn存放配置文件,默认情况下使用git。

2、Nacos替换Config

Nacos 可以与 Spring, Spring Boot, Spring Cloud 集成,并能代替 Spring Cloud Eureka, SpringCloud Config。通过 Nacos Server 和 spring-cloud-starter-alibaba-nacos-config 实现配置的动态变更。
(1)应用场景
在系统开发过程中,开发者通常会将一些需要变更的参数、变量等从代码中分离出来独立管理,以独立的配置文件的形式存在。目的是让静态的系统工件或者交付物(如 WAR,JAR 包等)更好地和实际的物理运行环境进行适配。配置管理一般包含在系统部署的过程中,由系统管理员或者运维人员完成。配置变更是调整系统运行时的行为的有效手段。如果微服务架构中没有使用统一配置中心时,所存在的问题:

  • 配置文件分散在各个项目里,不方便维护
  • 配置内容安全与权限
  • 更新配置后,项目需要重启

nacos配置中心:

系统配置的集中管理(编辑、存储、分发)、动态更新不重启、回滚配置(变更管理、历史版本管理、变更审计)等所有与配置相关的活动。

二、读取 配置中心的配置文件

1、在Nacos创建统一配置文件

(1)点击创建按钮

image-20210311113437242

(2)输入配置信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kjMYXomG-1615449307176)(http://qpgf4uqra.hn-bkt.clouddn.com/20210311113559.png)]

Data ID 的完整规则格式如下

p r e f i x − {prefix}- prefix{spring.profiles.active}.${file-extension}

image-20210311113528149


2、以service-statistics模块为例

(1)在 service中引入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

(2)创建bootstrap.properties配置文件

#配置中心地址
spring.cloud.nacos.config.server-addr=localhost:8848

#spring.profiles.active=dev

# 该配置影响统一配置中心中的dataId
spring.application.name=service-statistics

image-20210311114128511

image-20210311114134479

3)把项目之前的application.properties内容注释,启动项目查看效果

image-20210311114220183

3、补充:springboot配置文件加载顺序

其实 yml 和 properties 文件是一样的原理,且一个项目上要么 yml 或者 properties ,二选一的存在。推荐使用 yml ,更简洁。

bootstrap 与 application

(1)加载顺序

这里主要是说明 application 和 bootstrap 的加载顺序。

  • bootstrap.yml ( bootstrap.properties )先加载

  • application.yml ( application.properties )后加载

  • bootstrap.yml 用于应用程序上下文的引导阶段。

  • bootstrap.yml 由父 Spring ApplicationContext 加载。

  • 父 ApplicationContext 被加载到使用 application.yml 的之前。

(2)配置区别

bootstrap.yml 和 application.yml 都可以用来配置参数。

bootstrap.yml 可以理解成系统级别的一些参数配置,这些参数一般是不会变动的。

application.yml 可以用来定义应用级别的。

用yml需要在bootstrap中加入file-extension: yml


三、名称空间切换环境

在实际开发中,通常有多套不同的环境(默认只有public),那么这个时候可以根据指定的环境来创建不同的 namespce,

例如,开发、测试和生产三个不同的环境,那么使用一套 nacos 集群可以分别建以下三个不同的 namespace。以此来实现多环境的隔离。

1、创建命名空间

image-20210311115729537

默认只有public ,新建了 dev 、 test 和 prod 命名空间

image-20210311115741515

2、克隆配置

(1)切换到配置列表:

image-20210311115752473

可以发现有四个名称空间:public(默认)以及我们自己添加的3个名称空间(prod、dev、test),可以点击查看每个名称空间下的配置文件,当然现在只有public下有一个配置。默认情况下,项目会到public下找 服务名.properties文件。
接下来,在dev名称空间中也添加一个nacos-provider.properties配置。

这时有两种方式:

  • 第一, 切换到dev名称空间,添加一个新的配置文件。缺点:每个环境都要重复配置类似的项目

  • 第二, 直接通过clone方式添加配置,并修改即可。推荐

image-20210311115829708

点击编辑:修改配置内容,端口号改为8013以作区分

image-20210311115836944

在项目模块中,修改bootstrap.properties添加如下配置

指定一下名称空间是什么

spring.cloud.nacos.config.namespace=925a4338-5327-4967-8720-65412f8fc0df

namespace的值为:

image-20210311120733970

重启服务提供方服务,测试修改之后是否生效


四、多配置文件加载

在一些情况下需要加载多个配置文件。假如现在dev名称空间下有三个配置文件:

service-statistics.properties、redis.properties、jdbc.properties

image-20210311124112293

添加配置,加载多个配置文件

spring.cloud.nacos.config.server-addr=127.0.0.1:8848 

spring.profiles.active=dev

# 该配置影响统一配置中心中的dataId,之前已经配置过
spring.application.name=service-statistics

spring.cloud.nacos.config.namespace=13b5c197-de5b-47e7-9903-ec0538c9db01

spring.cloud.nacos.config.ext-config[0].data-id=port.properties
# 开启动态刷新配置,否则配置文件修改,工程无法感知
spring.cloud.nacos.config.ext-config[0].refresh=true

提交代码到远程Git仓库

image-20210311130410813

1、准备 Git 仓库

码云:https://gitee.com

1.1. 通过网站右上角的「+」号,选择「新建仓库」,进入新建仓库页面

image-20210311130856125

1.2. 新建仓库

image-20210311131228328

1.3. 打开项目并点击菜单栏上的【CVS】–》【Import into versioncontrol】–》【Create Git Repository】

创建本地Git仓库

img

1.4. 在打开的【Create Git Repository】对话框内选择本地仓库的位置,这里我选择项目的根目录。

image-20210311131806501

1.5. 右击项目点击【Git】–》【Add】,接着点击【Git】–》【Commit Directory】在打开的窗口中选择要上传到本地仓库的代码并添加注释后提交到本地仓库内。

将代码添加到本地Git库中

image-20210311132025118

1.6. 右击项目点击【Git】–》【Repository】–》【Remotes…】。在打开的【Git Remotes】窗口中添加码云的远程仓库。码云的远程仓库地址可以在码云仓库内找到。

img

设置远程Git库地址

image-20210311132533455

1.8. 上传代码到码云,右击项目点击【Git】–》【Repository】–》【Push…】在打开的【Pushcommits】内可以看到已提交到本地仓库的提交信息。点击【Push】按钮将本地仓库的代码上传到码云上,上传成功后就可以在码云上看到啦。

image-20210311132749722

image-20210311132818904

image-20210311132855758


项目部署

1. 准备代码,提交到码云Git库

代码中需要包含以下几部分内容:

(1)代码中需要包含Dockerfile文件

image-20210311141657494

文件内容

FROM openjdk:8-jdk-alpine

VOLUME /tmp

COPY ./target/demojenkins.jar demojenkins.jar【以后只需要修改这里的目录 和对象即可】

ENTRYPOINT ["java","-jar","/demojenkins.jar", "&"]

(2)在项目pom文件中指定打包类型,包含build部分内容

image-20210311141706882

image-20210311141711006


2、安装JAVA 运行环境

  • 第一步:上传或下载安装包

    cd/usr/local
    
    jdk-8u121-linux-x64.tar.gz
    
  • 第二步:解压安装包

    tar -zxvf jdk-8u121-linux-x64.tar.gz
    
  • 第三步:建立软连接

    ln -s /usr/local/jdk1.8.0_121/ /usr/local/jdk
    
  • 第四步:修改环境变量

    vim /etc/profile
    export JAVA_HOME=/usr/local/jdk
    export JRE_HOME=$JAVA_HOME/jre
    export CLASSPATH=.:$CLASSPATH:$JAVA_HOME/lib:$JRE_HOME/lib
    export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
    
    # 通过命令source /etc/profile让profile文件立即生效
    source /etc/profile
    
  • 第五步、测试是否安装成功
    ②、使用java -version,出现版本


3、安装maven

  • 第一步:上传或下载安装包

    cd/usr/local
    
    apache-maven-3.6.1-bin.tar.gz
    
  • 第二步:解压安装包

    tar -zxvf apache-maven-3.6.1-bin.tar.gz
    
  • 第三步:建立软连接
    ln -s /usr/local/apache-maven-3.6.1/ /usr/local/maven

  • 第四步:修改环境变量

    vim /etc/profile
    export MAVEN_HOME=/usr/local/maven
    export PATH=$PATH:$MAVEN_HOME/bin
    
    # 通过命令source /etc/profile让profile文件立即生效
    source /etc/profile
    
  • 第五步、测试是否安装成功

    mvn –v
    

4、安装git

yum -y install git

5、安装docker

  • 参考文档:
    https://help.aliyun.com/document_detail/60742.html?spm=a2c4g.11174283.6.548.24c14541ssYFIZ

  • 第一步:安装必要的一些系统工具

    yum install -y yum-utils device-mapper-persistent-data lvm2
    
  • 第二步:添加软件源信息

    yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    
  • 第三步:更新并安装Docker-CE

    yum makecache fast
    yum -y install docker-ce
    
  • 第四步:开启Docker服务

    service docker start
    
  • 第五步、测试是否安装成功

    docker -v
    

6、安装Jenkins

  • 第一步:上传或下载安装包

    cd/usr/local/jenkins
    
    jenkins.war
    
  • 第二步:启动

    nohup java -jar /usr/local/jenkins/jenkins.war >/usr/local/jenkins/jenkins.out &
    

image-20210311143839029

  • 第二步:访问
    http://ip:8080

7、初始化 Jenkins 插件和管理员用户

7.1访问jenkins

http://ip:8080

7.2解锁jenkins

获取管理员密码

image-20210311144646783

image-20210311144658510

image-20210311144704176

这里就可以关闭浏览器,然后配置下面的镜像

注意:配置国内的镜像

官方下载插件慢 更新下载地址

cd {你的Jenkins工作目录,一般是/root/.jenkins}/updates #进入更新配置位置
sed -i 's/http:\/\/updates.jenkins-ci.org\/download/https:\/\/mirrors.tuna.tsinghua.edu.cn\/jenkins/g'
default.json && sed -i 's/http:\/\/www.google.com/https:\/\/www.baidu.com/g' default.json

这是直接修改的配置文件,如果前边Jenkins用sudo启动的话,那么这里的两个sed前均需要加上sudo重启Jenkins,安装插件

再次重启jenkins,然后在访问地址

7.3选择“继续”

image-20210311150112775

7.4选择“安装推荐插件”

image-20210311150118581

7.5插件安装完成,创建管理员用户

image-20210311150135159

7.6保存并完成

image-20210311150152138

7.7 进入完成页面

image-20210311150200147


8、配置 Jenkins 构建工具

image-20210311150658912

8.1.1配置jdk

JAVA_HOME:/usr/local/jdk

image-20210311151033431

8.1.2配置maven

MAVEN_HOME:/usr/local/maven

image-20210311151126522

8.1.2配置git

查看git安装路径:which git

image-20210311151202245


9、构建作业

9.1点击创建一个新任务,进入创建项目类型选择页面

image-20210311151951004

image-20210311152031692

填好信息点击“确认”

9.2配置“General”

image-20210311152348828

9.3配置“源码管理”

image-20210311152336922

配置git远程仓库地址,与git远程仓库的账户密码

image-20210311152401894

9.4构建作业

到源码中找到docker脚本

选择“执行shell”,通过脚本的方式构建

image-20210311152515655

保存上面的构建作业

9.5构建

构建作业之后,就可以执行构建过程了。

9.5.1执行构建过程

image-20210311153417376

9.5.2构建结构

image-20210311153625560

9.5.3查看控制台输出

image-20210311153837688

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nAlYoxNR-1615449307268)(http://qpgf4uqra.hn-bkt.clouddn.com/20210311153850.png)]

image-20210311154738084


Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐