感觉现在的Java开发人员已经Spring框架,不讨论这种事情是好是坏,但是确实好用,但是个人入门有些东西难度还是很高的,摸索的进度有些许的慢,只能慢慢的更新了,那今天就慢慢更新一期SpringSecurity的入门篇,完成完整的系统,可以直接投入生产开发使用。

如何开始

  1. 有一定的Java基础,会Spring BootSpring SecurityMyBatis plus
  2. 首先,明白什么是token
  3. token有什么样的作用
  4. 了解基础的角色权限验证

Token

先来体验一下什么是 token

package com.shaojie.authority.jwt;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.IdUtil;
import com.shaojie.authority.exception.TokenException;
import com.shaojie.authority.service.impl.AuthorityServiceImpl;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import java.sql.Date;

/**
 * @author: ShaoJie
 * @data: 2020年02月22日 18:17
 * @Description: jwt 生成校验 token
 */
@Slf4j
@Configuration
public class JwtUtil {

    /**
     * 认证关键key
     */
    String signingKey = "SigningKey";

    /**
     * 创建生成 token
     *
     * @return
     */
    public String createToken() {
        //30秒过期
        long now = System.currentTimeMillis();
        long exp = now + 1000 * 60;
        // builder 构建 token
        String token = Jwts.builder()
                // 设置唯一的 id
                .setId(IdUtil.simpleUUID())
                // 设置主题
                .setSubject("token")
                // 设置角色
                .claim("authorities", "admin")
                // 设置角色集
//                .addClaims()
                // 设置过期时间
                .setExpiration(new Date(exp))
                // 设置 token 签发的时间
                .setIssuedAt(new DateTime())
                // 设置签名 使用HS256算法,并设置SecretKey(字符串)  签名算法和秘钥
                .signWith(SignatureAlgorithm.HS256, signingKey)
                // 以下内容构建JWT并将其序列化为紧凑的,URL安全的字符串
                .compact();
        log.info("token:{}", token);
        return token;
    }

    /**
     * 解析 token
     *
     * @param token 用户的 token
     */
    public void parseToken(String token) throws TokenException {
        Claims claims = Jwts.parser().setSigningKey(signingKey).parseClaimsJws(token).getBody();
        if (claims.equals(null))
            throw new TokenException();
        log.info("解析的数据:{}", claims);
    }

    public static void main(String[] args) {
        JwtUtil jwtUtil = new JwtUtil();
        String token = jwtUtil.createToken();
        try {
            jwtUtil.parseToken(token);
        } catch (TokenException e) {
            log.info("token 过期");
        }
        System.out.println(token);
    }

}
生成的token:
eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJiZmNjYzRlOWQ5OWY0Mzc3YTI1MjBmNjkxZDM1NzkyMiIsInN1YiI6InRva2VuIiwiYWRtaW4iOiJTaGFvSmllIiwiZXhwIjoxNTk1NDA0MTU4LCJpYXQiOjE1OTU0MDQwOTh9.AjPDjf40BnAzgnU3mCpjMI8KYggEVR8264JATKg4cFc

解析到的token:
{jti=bfccc4e9d99f4377a2520f691d357922, sub=token, admin=ShaoJie, exp=1595404158, iat=1595404098}

解析创建的token不难发现其中包含的信息:当前的创建token的主题、角色、过期时间等

这只是学习运用的第一步,体验这种认证带来的方便,和便捷。然后进入到正题,开始更新这样的一期Spring Boot整合SpringSecurity

基础依赖

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>top.lzmvlog</groupId>
    <artifactId>authority</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>authority</name>
    <description>Spring Boot 整合 JWT 做授权认证</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR4</spring-cloud.version>
    </properties>

    <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.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.22</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.6</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>3.3.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

JwtUtil 创建校验工具

package top.lzmvlog.authority.util.jwt;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpStatus;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import top.lzmvlog.authority.exception.TokenException;
import top.lzmvlog.authority.util.date.DateUtil;

/**
 * @author ShaoJie
 * @Date 2020年05月12 15:16
 * @Description: 生成解析 token 的工具类
 */
@Component
@Slf4j
public class JwtUtil {

    /**
     * 签名密钥
     */
    @Value("${auth.token.signingKey}")
    private String signingKey;

    /**
     * 创建生成 token
     *
     * @return String 生成的 token
     */
    public String createToken(String account) {
        log.info("账号:{} 登录成功", account);
        return Jwts.builder()
                // 设置唯一的 ida
                .setId(IdUtil.simpleUUID())
                // 设置主要包含的信息
                .setSubject(account)
                // 设置过期时间
                .setExpiration(new DateUtil().getNowDateOneTime())
                // 设置 token 签发的时间
                .setIssuedAt(new DateTime())
                // 设置签名 使用HS256算法,并设置SecretKey(字符串)  签名算法和秘钥
                .signWith(SignatureAlgorithm.HS256, signingKey)
                // 以下内容构建JWT并将其序列化为紧凑的,URL安全的字符串
                .compact();
    }

    /**
     * 解析当前的 token
     *
     * @param token token 信息
     * @return String token种解析到的信息
     */
    public String parseToken(String token) {
        Claims claims = Jwts.parser().setSigningKey(signingKey).parseClaimsJws(token).getBody();
        if (claims.equals(null))
            throw new TokenException(HttpStatus.HTTP_INTERNAL_ERROR, "token 信息错误 重新授权");
        return claims.getSubject();
    }

}

当前的JwtUtil并没有加入关于角色的任何信息,只是单纯的针对账号去授权,生成这样的一个token,当然你也可以将你的权限信息去进行授权,自行发散思维。

构建权限验证

package top.lzmvlog.authority.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import top.lzmvlog.authority.filter.JwtAuthenticationFilter;
import top.lzmvlog.authority.handler.JwtAccessDeniedHandler;

/**
 * @author ShaoJie
 * @Date 2020年05月12 14:36
 * @Description: 安全验证配置
 */
@Configuration
@EnableWebSecurity
public class SecurityVerificationConfiguration extends WebSecurityConfigurerAdapter {

    /**
     * 密码加密
     *
     * @return
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 拦截器
     */
    @Autowired
    public JwtAuthenticationFilter jwtAuthenticationFilter;

    /**
     * jwt 验证处理器
     */
    @Autowired
    public JwtAccessDeniedHandler jwtAccessDeniedHandler;

    /**
     * toekn 配置
     */
    @Autowired
    public TokenConfiguration tokenConfiguration;

    /**
     * 授权 、 验证
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                // 授权地址不需要验证
                .antMatchers("/auth/token").permitAll()
                // 用户注册地址
                .antMatchers("/user/registered").permitAll()
                // 其余的都需要校验
                .anyRequest().authenticated()
                .and()
                // 添加后置处理拦截器
                .addFilterAfter(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .exceptionHandling()
                // 访问拒绝处理程序
                .accessDeniedHandler(jwtAccessDeniedHandler)
                .and()
                .apply(tokenConfiguration)
                .and()
                // 取消 session 的状态
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .csrf().disable();
    }

}

BCryptPasswordEncoder官方提供的密码加密类,对注册的用户信息进行加密,例如用户的登录密码。

JwtAuthenticationFilter权限拦截器,用于拦截用户信息,进行权限校验。

JwtAccessDeniedHandler拒绝访问处理器。

TokenConfiguration适配器配置,用于修改默认的认证处理器,实现自定义的验证

下次更新将着重对这些类细谈其实现,尝鲜体验可访问我的 GitHub 查看 authority项目 clone

$ git clone https://github.com/lzmvlog/authority.git