微服务项目构建认证中心(SpringSecurity+JWT)

前言,毕设项目中,由于只考虑web端的,没考虑客户端,所以没有用oauth2(其实也是因为没掌握,尴尬)

一、项目准备

  1. 父项目,建立统一管理,pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.jiangming</groupId>
        <artifactId>hg_blog</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        
        <packaging>pom</packaging>
        <description>a blog web</description>
    
        <properties>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <java.version>1.8</java.version>
            <junit.version>4.12</junit.version>
            <log4j.version>1.2.17</log4j.version>
            <lombok.version>1.18.20</lombok.version>
            <mysql.version>8.0.26</mysql.version>
            <druid.version>1.2.5</druid.version>
            <alibaba.fastjson.version>1.2.61</alibaba.fastjson.version>
            <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
            <spring-boot.version>2.3.2.RELEASE</spring-boot.version>
            <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
            <spring-cloud-alibaba.version>2.2.5.RELEASE</spring-cloud-alibaba.version>
        </properties>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-dependencies</artifactId>
                    <version>${spring-boot.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
    
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
    
                <dependency>
                    <groupId>com.alibaba.cloud</groupId>
                    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                    <version>${spring-cloud-alibaba.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
    
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>${mysql.version}</version>
                </dependency>
    
                <dependency>
                    <groupId>com.alibaba</groupId>
                    <artifactId>druid-spring-boot-starter</artifactId>
                    <version>${druid.version}</version>
                </dependency>
    
                <dependency>
                    <groupId>org.mybatis.spring.boot</groupId>
                    <artifactId>mybatis-spring-boot-starter</artifactId>
                    <version>${mybatis.spring.boot.version}</version>
                </dependency>
    
                <dependency>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                    <version>${junit.version}</version>
                </dependency>
    
                <dependency>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                    <version>${log4j.version}</version>
                </dependency>
    
                <dependency>
                    <groupId>org.projectlombok</groupId>
                    <artifactId>lombok</artifactId>
                    <version>${lombok.version}</version>
                    <optional>true</optional>
                </dependency>
    
                <dependency>
                    <groupId>com.alibaba</groupId>
                    <artifactId>fastjson</artifactId>
                    <version>${alibaba.fastjson.version}</version>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <fork>true</fork>
                        <addResources>true</addResources>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
  2. 子项目pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>hg_blog</artifactId>
            <groupId>com.jiangming</groupId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>hegu-auth-jwt</artifactId>
    
        <dependencies>
            <!--自定义公共包-->
            <dependency>
                <groupId>com.jiangming</groupId>
                <artifactId>hegu-api-commons</artifactId>
                <version>0.0.1-SNAPSHOT</version>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>0.9.1</version>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
    
    </project>
    
  3. 项目结构

    在这里插入图片描述

  4. nacos配置文件

    在这里插入图片描述

    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://IP地址:3302/数据库名?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC&characterEncoding=utf-8
        username: root
        password: 123456
        druid:
          driver-class-name: com.mysql.cj.jdbc.Driver
          initial-size: 10
          max-active: 100
          min-idle: 10
          max-wait: 1000
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'
    

二、代码

  1. application.yml

    spring:
      profiles:
        active: dev
    
  2. bootstrap.yml

    server:
      port: 端口号
    
    spring:
      application:
        name: 服务名
      cloud:
        nacos:
          discovery:
            server-addr: nacos地址
            #命名空间
            namespace: ce58f19d-b36c-4aad-968a-1a1f2280cfc
          config:
            server-addr: nacos地址
            #命名空间
            namespace: ce58f19d-b36c-4aad-968a-1a1f2280cfc
            file-extension: yaml
    
  3. 主启动类

    package com.jc.springcloud;
    
    /**
     * @program: hg_blog
     * @description:
     * @author: hjc
     * @create: 2022-01-02 19:47
     **/
    @SpringBootApplication
    @EnableDiscoveryClient
    public class HeguAuthJwtMain {
        public static void main(String[] args){
            SpringApplication.run(HeguAuthJwtMain.class,args);
        }
    }
    
    
  4. JwtUtil工具类

    package com.jc.springcloud.common;
    
    /**
     * @program: hg_blog
     * @description: 生成token
     * @author: hjc
     * @create: 2022-01-04 13:31
     **/
    public class JwtUtil {
    
        private static final String JWT_PAYLOAD_USER_KEY = "user";
    
        // 私钥,RSA在线生成工具
        private static final String PRIVATE_KEY ="";
    
        /***
         * @description  私钥
         * @return java.security.PrivateKey
         * @author hjc
         * CreateDate 2022/1/5 15:15
         */
        public static PrivateKey getPrivateKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(PRIVATE_KEY));
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return keyFactory.generatePrivate(spec);
        }
    
        /***
         * @description 生成token
         * @param userInfo 用户信息
         * @param privateKey 私钥对象
         * @param expire 过期时间
         * @return java.lang.String
         * @author hjc
         * CreateDate 2022/1/4 13:34
         */
        public static <T> String createToken(T userInfo, PrivateKey privateKey,int expire) throws JsonProcessingException{
            //设置过期时间
            //获取当前时间
            Calendar calendar = Calendar.getInstance();
            //在当前时间加上过期时间
            calendar.add(Calendar.SECOND,expire);
            //将userInfo对象转为Json
            String userInfoJson = new ObjectMapper().writeValueAsString(userInfo);
            //生成token
    
            return Jwts.builder()//获得Jwt的编译对象
                    .claim(JWT_PAYLOAD_USER_KEY,userInfoJson)//设置负载,负载内容为JSON字符串
                    .setExpiration(calendar.getTime())//设置过期时间
                    .signWith(SignatureAlgorithm.RS256, privateKey)//设置签名,使用私钥做为签名
                    .compact();//生成token
        }
    }
    
  5. SysUserDetails扩展类

    package com.jc.springcloud.common;
    
    /**
     * @program: hg_blog
     * @description:
     * @author: hjc
     * @create: 2022-01-03 14:40
     **/
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class SysUserDetails implements UserDetails {
    
        //扩展字段
        private Integer user_id;
    
        //默认字段
        private String username;
        private String password;
        private boolean isAccountNonLocked;
        private Collection<? extends GrantedAuthority> authorities;
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return this.authorities;
        }
    
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
        @Override
        public boolean isEnabled() {
            return true;
        }
    }
    
    
  6. SecurityConfig类

    package com.jc.springcloud.config;
    
    /**
     * @program: hg_blog
     * @description: Security配置
     * @author: hjc
     * @create: 2022-01-02 19:50
     **/
    @Configuration
    @EnableWebSecurity
    @Slf4j
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Bean
        public PasswordEncoder passwordEncoder(){
            return new BCryptPasswordEncoder();
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .cors()//开启security对跨域的支持
                .and()
                    .formLogin()//登录设置
                    .loginPage("/login")//登录处理器
                .and()
                    .csrf()
                    .disable();//禁用跨域伪造检测
            http.addFilter(new BlogUsernamePasswordAuthenticationFilter(authenticationManager()));
        }
    }
    
  7. BlogUsernamePasswordAuthenticationFilter

    package com.jc.springcloud.filter;
    
    /**
     * @program: hg_blog
     * @description: 当用户登录时会自动执行该过滤器
     * @author: hjc
     * @create: 2022-01-04 14:26
     **/
    @Slf4j
    public class BlogUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    
        //过期时间一天
        private final int ACTIVE_TIME = 60 * 60 * 24;
    //    private final int ACTIVE_TIME = 60 * 2;
    
        //设置认证管理器属性
        private AuthenticationManager authenticationManager;
    
        public BlogUsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager){
            this.authenticationManager = authenticationManager;
        }
    
        /***
         * @description 用于获取客户端提交的数据,提交的数据处理后会交给UserDetailsService继续执行
         * @param request
         * @param response
         * @return org.springframework.security.core.Authentication
         * @author hjc
         * CreateDate 2022/1/4 14:45
         */
        @SneakyThrows
        @Override
        public Authentication attemptAuthentication(HttpServletRequest request,
                                                        HttpServletResponse response) throws AuthenticationException {
            log.info("attemptAuthentication被执行");
    
            //获取客户端传入的payload参数
            LoginInfo loginInfo = new ObjectMapper().readValue(request.getInputStream(), LoginInfo.class);
    
            log.info(String.valueOf(loginInfo));
    
            //创建认证令牌并将认证信息传入
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginInfo.getUsername(), loginInfo.getPassword());
            //将认证令牌提交给认证管理器,认证管理器会将authRequest交给UserDetailsService处理
            return authenticationManager.authenticate(authenticationToken);
        }
    
        /***
         * @description 当用户登录成功后执行该方法,它在UserDetailsService认证成功后执行
         * 在该方法中使用私钥生成token,并将token发送到客户端
         * @param request
         * @param response
         * @param chain
         * @param authResult
         * @return void
         * @author hjc
         * CreateDate 2022/1/4 14:48
         */
        @SneakyThrows
        @Override
        protected void successfulAuthentication(HttpServletRequest request,
                                                    HttpServletResponse response,
                                                    FilterChain chain,
                                                    Authentication authResult) throws IOException, ServletException {
            log.info("登录成功!");
            response.setContentType("application/json;charset=utf-8");
    
            //从认证信息中获取用户数据,并设置在payload集合中
    
            SysUserDetails sysUserDetails = (SysUserDetails) authResult.getPrincipal();
            log.info(String.valueOf(sysUserDetails));
    
            //创建jwt负载的map集合并设置相关数据
            Map<String, Object> payloadMap = new HashMap<String, Object>(){{
                put("user_id",sysUserDetails.getUser_id());//当前登录者的id
                put("auth",sysUserDetails.getAuthorities());//当前账户的权限集合
            }};
    
            //获取私钥
            PrivateKey privateKey = JwtUtil.getPrivateKey();
            //使用私钥和过期时间生成token
            String token = JwtUtil.createToken(payloadMap,privateKey,ACTIVE_TIME);
            PrintWriter writer = response.getWriter();
            writer.print(JSON.toJSONString(CommonResult.success("登录成功!",token)));
            writer.flush();
            writer.close();
        }
    
        /***
         * @description 用户登录失败后执行该方法,它在UserDetailsService认证失败后执行
         * @param request
         * @param response
         * @param failed
         * @return void
         * @author hjc
         * CreateDate 2022/1/4 15:55
         */
        @Override
        protected void unsuccessfulAuthentication(HttpServletRequest request,
                                                    HttpServletResponse response,
                                                    AuthenticationException failed) throws IOException, ServletException {
            log.info("登录失败,unsuccessfulAuthentication");
            response.setContentType("application/json;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.print(JSON.toJSONString(CommonResult.fail(HttpServletResponse.SC_UNAUTHORIZED,failed.getMessage())));
            writer.flush();
            writer.close();
        }
    }
    
  8. AuthMapper

    package com.jc.springcloud.mapper;
    
    /**
     * @program: hg_blog
     * @description:
     * @author: hjc
     * @create: 2022-01-03 12:35
     **/
    @Mapper
    public interface AuthMapper {
    
        //登录
        @Select("select user_id,user_phone,user_password,is_superuser,is_lock from hb_user where user_phone = #{user_phone}")
        public HbUserInfo queryLoginByPhone(String user_phone);
    }
    
    
  9. SysUserDetailsServiceImpl

    package com.jc.springcloud.service.impl;
    
    /**
     * @program: hg_blog
     * @description: UserDetailsService实现类,该类交给Spring管理后,Security会在登录时自动查找UserDetailsService的实现类
     * @author: hjc
     * @create: 2022-01-03 14:47
     **/
    @Slf4j
    @Service
    public class SysUserDetailsServiceImpl implements UserDetailsService {
    
        @Resource
        private AuthMapper authMapper;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    
            log.info("执行UserDetailsServiceImpl");
    
            //根据用户名及手机号查询用户信息
            HbUserInfo hbUserInfo = authMapper.queryLoginByPhone(username);
    
            if (hbUserInfo == null){
                throw new UsernameNotFoundException("用户不存在!");
            }
    
            Collection<GrantedAuthority> authorities = new ArrayList<>();
            if (hbUserInfo.getIs_superuser() == 0){
                authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
            }else {
                authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
            }
    
        return new SysUserDetails(
                hbUserInfo.getUser_id(),
                hbUserInfo.getUser_phone(),
                hbUserInfo.getUser_password(),
                hbUserInfo.getIs_lock() == 0 ? true : false,
                authorities);
        }
    }
    

三、测试

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
网关统一鉴权中心

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叁Mar.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值