Jwt,Spring Security,Spring Cloud OAuth2,Spring Cloud Security

首先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 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>1.5.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.springsecurityjwt</groupId>
	<artifactId>springsecurityjwt</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>springsecurityjwt</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<!--<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>-->
		<!--<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>-->
        <security-jwt.version>1.0.9.RELEASE</security-jwt.version>
		<hutool.version>4.5.0</hutool.version>
        <jjwt.version>0.9.0</jjwt.version>
    </properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
            <version>${security-jwt.version}</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jjwt.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.7.0</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.7.0</version>
		</dependency>
		<!-- 超级工具类 hutool -->
		<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-all</artifactId>
			<version>${hutool.version}</version>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Dalston.RC1</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>
	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>


</project>

测试数据

/*
Navicat MySQL Data Transfer

Source Server         : 本机mysql
Source Server Version : 50713
Source Host           : localhost:3306
Source Database       : zmx

Target Server Type    : MYSQL
Target Server Version : 50713
File Encoding         : 65001

Date: 2019-09-23 15:14:09
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for menu
-- ----------------------------
DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu` (
  `menu_id` bigint(20) NOT NULL,
  `permission` varchar(255) DEFAULT NULL,
  `url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`menu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of menu
-- ----------------------------
INSERT INTO `menu` VALUES ('4', '所有权限', '/test');

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
  `role_id` bigint(20) NOT NULL,
  `role_name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES ('2', '管理员');

-- ----------------------------
-- Table structure for role_menu
-- ----------------------------
DROP TABLE IF EXISTS `role_menu`;
CREATE TABLE `role_menu` (
  `role_menu_id` bigint(20) NOT NULL,
  `menu_id` bigint(20) NOT NULL,
  `role_id` bigint(20) NOT NULL,
  PRIMARY KEY (`role_menu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of role_menu
-- ----------------------------
INSERT INTO `role_menu` VALUES ('5', '4', '2');
INSERT INTO `role_menu` VALUES ('6', '4', '2');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `user_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `avatar` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `phone` varchar(255) DEFAULT NULL,
  `realname` varchar(255) DEFAULT NULL,
  `sex` varchar(255) DEFAULT NULL,
  `username` varchar(255) DEFAULT NULL,
  `authorities` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', null, null, '123456', null, null, null, 'admin', null);
INSERT INTO `user` VALUES ('2', null, null, '$2a$10$wk3UUqG2IvU67w9Lj/EUOO7lEOB9AMqcXMZO.Lqz2K3NA4WXppNSe', null, null, null, 'user', null);
INSERT INTO `user` VALUES ('3', null, null, '$2a$10$LhBOkwQe9EqqDWezMxm62eyrfUL.9SZnpmBXPDW3zEYaQH1Hwsr8G', null, null, null, 'zhao', null);

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
  `user_role_id` bigint(20) NOT NULL,
  `role_id` bigint(20) NOT NULL,
  `user_id` bigint(20) NOT NULL,
  PRIMARY KEY (`user_role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES ('3', '2', '1');

 

第一种

一、增加JWT认证功能

用户在调用登录接口,输入用户名和密码之后,如果通过便认证通过。传统的方法是在认证通过之后,创建session并返回客户端cookie。这里采用JWT来处理用户密码的认证。区别在于,认证通过之后服务器生成的是token,将token返回给客户端,客户端以后的所有请求都需要在http头中指定该token。服务器接收请求后,会对token的合法性进行验证。

验证的内容包括:1.内容是一个正确的JWT格式2.检查签名3.检查权限4.处理登录

这一步需要创建一个JWTLoginFilter该类继承UsernamePasswordAuthenticationFilter,核心功能是在验证用户名密码正确之后,生成一个token,并将token返回给客户端

JWTLoginFilter.java

package com.springsecurityjwt.springsecurityjwt.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.springsecurityjwt.springsecurityjwt.constant.ConstantKey;
import com.springsecurityjwt.springsecurityjwt.exception.ExceptionEnum;
import com.springsecurityjwt.springsecurityjwt.exception.UnifiedException;
import com.springsecurityjwt.springsecurityjwt.model.User;
import com.springsecurityjwt.springsecurityjwt.model.UserPrincipal;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.tomcat.util.codec.binary.Base64;
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.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
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.*;

/**
 * 验证用户名密码正确之后,生成一个token,并将token返回给客户端,该方法继承自UsernamePasswordAuthenticationFilter。
 * 重写其中的两个方法attemptAuthentication :接收并解析用户凭证。
 * successfulAuthentication :用户成功登录后,这个方法会被调用,我们在这个方法里生成token。
 *
 * @Author zhaomengxia
 * @create 2019/9/20 14:38
 */
public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;
    private static final String JWT_SECRET = ConstantKey.SIGNING_KEY;// JWT密匙
    /**
     * 由字符串生成加密key
     *
     * @return
     */
    public static SecretKey generalKey() {
        String stringKey = JWT_SECRET;
        byte[] encodedKey = Base64.decodeBase64(stringKey);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }
    public JWTLoginFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }


    //接收并解析用户凭证
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            User user = new ObjectMapper().readValue(request.getInputStream(), User.class);

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

    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {

        String token = null;
        try {
            Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities();

            List roleList = new ArrayList();

            for (GrantedAuthority grantedAuthority :
                    authorities) {
                roleList.add(grantedAuthority.getAuthority());
            }
            Calendar calendar = Calendar.getInstance();
            Date now = calendar.getTime();
            //设置签发时间  当前时间
            calendar.setTime(new Date());

            calendar.add(Calendar.HOUR, 10);//设置10个小时
            Date time = calendar.getTime();
            SecretKey secretKey = generalKey();
            token = Jwts.builder().setSubject(authResult.getName() + "-" + roleList)
                    .setIssuedAt(now)//签发时间
                    .setExpiration(time)//token有效时长
                    .signWith(SignatureAlgorithm.HS256,secretKey)
                    .compact();
            response.addHeader("Authorization", "Bearer " + token);
            System.out.println("Bearer " + token);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

二、授权验证

拿到token之后,请求接口会携带这个token,服务端要验证这个token的合法性。创建一个类JwtAuthenticationFilter并继承BasicAuthenticationFilter。在doFilterInternal方法中,从http头的Authorization项读取token数据,然后使用Jwts包提供的方法校验token的合法性。如果校验通过,就说明这是一个取得授权的合法请求。

JwtAuthenticationFilter.java

package com.springsecurityjwt.springsecurityjwt.filter;

import com.springsecurityjwt.springsecurityjwt.constant.ConstantKey;
import com.springsecurityjwt.springsecurityjwt.exception.TokenException;
import com.springsecurityjwt.springsecurityjwt.service.impl.GrantedAuthorityImpl;
import io.jsonwebtoken.*;
import org.apache.tomcat.util.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

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;


/**
 * @Author zhaomengxia  这个模块中登录有两种一种是使用spring specurity框架提供的默认登录url localhost:18081/login
 * 使用JwtAuthenticationTwoFilter这一个,注意WebSecurityConfig类里将 .addFilter(new JWTLoginFilter(authenticationManager()))
 *             和 .addFilter(new JwtAuthenticationTwoFilter(authenticationManager()))两个的注释放开,将 http.addFilterBefore(jwtAuthenticationTwoFilter(),UsernamePasswordAuthenticationFilter.class);注释
 *另一种是采用自己写的登录接口 url为http://localhost:18081/user/login 是将WebSecurityConfig中的http.addFilterBefore(jwtAuthenticationTwoFilter(),UsernamePasswordAuthenticationFilter.class);注释放开,将另外两个注释,本项目中是采用这一种方法
 * @create 2019/9/20 15:10
 */
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {

    private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
    // 指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。

    private static final String JWT_SECRET = ConstantKey.SIGNING_KEY;// JWT密匙

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String header = request.getHeader("Authorization");
        if (header == null || !header.startsWith("Bearer")) {
            chain.doFilter(request, response);
            return;
        }

        UsernamePasswordAuthenticationToken authenticationToken=  getAuthention(request);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        chain.doFilter(request, response);
    }


    private UsernamePasswordAuthenticationToken getAuthention(HttpServletRequest servletRequest){

        long start=System.currentTimeMillis();
        String token=servletRequest.getHeader("Authorization");
        if (token==null||token.isEmpty()){
            throw new TokenException("Token 不能为空!");
        }
        String stringKey = JWT_SECRET;
        String user=null;
        try {
        user= Jwts.parser()
                .setSigningKey(Base64.decodeBase64(stringKey))
                .parseClaimsJws(token.replace("Bearer ", ""))
                .getBody()
                .getSubject();
        long end=System.currentTimeMillis();
        logger.info("执行时间:{}",end-start+"ms");

        if (user!=null){
            String[] split=user.split("-")[1].split(",");
            ArrayList<GrantedAuthority> authorities=new ArrayList<>();
            for (int i=0;i<split.length;i++){
                authorities.add(new GrantedAuthorityImpl(split[i]));
            }
            return new UsernamePasswordAuthenticationToken(user,null,authorities);
        }
        } catch (ExpiredJwtException e) {
            logger.error("Token已过期: {} " + e);
            throw new TokenException("Token已过期");
        } catch (UnsupportedJwtException e) {
            logger.error("Token格式错误: {} " + e);
            throw new TokenException("Token格式错误");
        } catch (MalformedJwtException e) {
            logger.error("Token没有被正确构造: {} " + e);
            throw new TokenException("Token没有被正确构造");
        } catch (SignatureException e) {
            logger.error("签名失败: {} " + e);
            throw new TokenException("签名失败");
        } catch (IllegalArgumentException e) {
            logger.error("非法参数异常: {} " + e);
            throw new TokenException("非法参数异常");
        }

        return null;

    }
}

三、Spring Security

通过配置Spring Security将上面的方法组合在一起。

WebSecurityConfig.java
package com.springsecurityjwt.springsecurityjwt.config;

import com.springsecurityjwt.springsecurityjwt.filter.JWTLoginFilter;
import com.springsecurityjwt.springsecurityjwt.filter.JwtAuthenticationFilter;
import com.springsecurityjwt.springsecurityjwt.filter.JwtAuthenticationTwoFilter;
import com.springsecurityjwt.springsecurityjwt.handler.Http401AuthenticationEntryPoint;
import com.springsecurityjwt.springsecurityjwt.service.impl.CustomAuthenticationProvider;
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.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.annotation.web.configurers.LogoutConfigurer;
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.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @Author zhaomengxia
 * @create 2019/9/16 14:00
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{


    @Bean
    public JwtAuthenticationTwoFilter jwtAuthenticationTwoFilter(){
        return new JwtAuthenticationTwoFilter();
    }

    private static final String[] AUTH_WHITELIST = {
            // -- register url
            "/user/signup",
            "/user/addTask",
            // -- swagger ui
            "/v2/api-docs",
            "/swagger-resources",
            "/swagger-resources/**",
            "/configuration/ui",
            "/configuration/security",
            "/swagger-ui.html",
            "/user/login",
            "/login",
            "/webjars/**"
            // other public endpoints of your API may be appended to this array
    };

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        LogoutConfigurer<HttpSecurity> httpSecurityLogoutConfigurer=http.cors().and()
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(AUTH_WHITELIST).permitAll()
                .anyRequest().authenticated()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(new Http401AuthenticationEntryPoint("Basic realm=\"MyApp\""))
                .and()
                .addFilter(new JWTLoginFilter(authenticationManager()))
                .addFilter(new JwtAuthenticationFilter(authenticationManager()))
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login")
                .permitAll();

        //http.addFilterBefore(jwtAuthenticationTwoFilter(),UsernamePasswordAuthenticationFilter.class);
    }


    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(new CustomAuthenticationProvider(userDetailsService,bCryptPasswordEncoder));
    }
}
第一种
1.启动SpringsecurityjwtApplication.java类

2.在postman测试,注册一个用户
url:localhost:18081/user/signup
post
header: key--Content-Type ,values--application/json
body:{
       "username":"user",
       "password":"123456"
     }
3. 在postman登录
url: localhost:18081/login    //spring specurity框架提供的默认登录url
post
header: key--Content-Type ,values--application/json
body:{
    "username":"user",
    "password":"123456"
}

token值是 postman返回结果的header里的key--Authorization对应values
  
4. 浏览器访问 http://localhost:18081/swagger-ui.html#
   根据拿到的Authorization对应values
   放到swagger查询用户信息接口http://localhost:18081/user/findByUserName,Authorization,放token
   接着userName对应输入admin
   点击Try it out即可,根据返回结果 可以测试得出拿到的token可用不可用

用postman测试 localhost:18081/login    //spring specurity框架提供的默认登录url

结果

第二种

一、添加类

JwtTokenProvider.java  用户名密码验证通过之后,返回token的
package com.springsecurityjwt.springsecurityjwt.filter;

import com.springsecurityjwt.springsecurityjwt.constant.ConstantKey;
import io.jsonwebtoken.*;
import org.apache.tomcat.util.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * @Author zhaomengxia
 * @create 2019/9/23 13:30
 */
@Component
public class JwtTokenProvider {
    private static final String JWT_SECRET = ConstantKey.SIGNING_KEY;// JWT密匙
    private static final Logger logger = LoggerFactory.getLogger(JwtTokenProvider.class);
    /**
     * 由字符串生成加密key
     *
     * @return
     */
    public static SecretKey generalKey() {
        String stringKey = JWT_SECRET;
        byte[] encodedKey = Base64.decodeBase64(stringKey);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }
    public String successfulAuthentication(HttpServletResponse response,
                                            Authentication authResult) throws IOException, ServletException {

        String token = null;
        String authentication=null;
        try {
            Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities();

            List roleList = new ArrayList();

            for (GrantedAuthority grantedAuthority :
                    authorities) {
                roleList.add(grantedAuthority.getAuthority());
            }
            Calendar calendar = Calendar.getInstance();
            Date now = calendar.getTime();
            //设置签发时间  当前时间
            calendar.setTime(new Date());

            calendar.add(Calendar.HOUR, 10);//设置10个小时
            Date time = calendar.getTime();
            SecretKey secretKey = generalKey();
            token = Jwts.builder().setSubject(authResult.getName() + "-" + roleList)
                    .setIssuedAt(now)//签发时间
                    .setExpiration(time)//token有效时长
                    .signWith(SignatureAlgorithm.HS256,secretKey)
                    .compact();
            authentication="Bearer " + token;
            response.addHeader("Authorization", authentication);
            System.out.println(authentication);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return authentication;
    }
    public String getUserIdFromJWT(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(JWT_SECRET)
                .parseClaimsJws(token)
                .getBody();

        String[] s=claims.getSubject().split("-");

        return s[0];
    }

    public boolean validateToken(String authToken) {
        try {
            Jwts.parser().setSigningKey(JWT_SECRET).parseClaimsJws(authToken);
            return true;
        } catch (SignatureException ex) {
            logger.error("Invalid JWT signature");
        } catch (MalformedJwtException ex) {
            logger.error("Invalid JWT token");
        } catch (ExpiredJwtException ex) {
            logger.error("Expired JWT token");
        } catch (UnsupportedJwtException ex) {
            logger.error("Unsupported JWT token");
        } catch (IllegalArgumentException ex) {
            logger.error("JWT claims string is empty.");
        }
        return false;
    }
}

二、拿到token之后,每次请求验证token的

JwtAuthenticationTwoFilter.java
package com.springsecurityjwt.springsecurityjwt.filter;

import com.springsecurityjwt.springsecurityjwt.service.impl.UserDetailsServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
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;

/**
 * @Author zhaomengxia
 * @create 2019/9/23 13:48
 */
public class JwtAuthenticationTwoFilter extends OncePerRequestFilter {
    @Autowired
    private JwtTokenProvider tokenProvider;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationTwoFilter.class);

    public JwtAuthenticationTwoFilter() {
    }


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            String jwt = getJwtFromRequest(request);

            if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
                String userName = tokenProvider.getUserIdFromJWT(jwt);

                /*
                    Note that you could also encode the user's username and roles inside JWT claims
                    and create the UserDetails object by parsing those claims from the JWT.
                    That would avoid the following database hit. It's completely up to you.
                 */
                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 ex) {
            logger.error("Could not set user authentication in security context", ex);
        }

        filterChain.doFilter(request, response);
    }

    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader(HttpHeaders.AUTHORIZATION);
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7, bearerToken.length());
        }
        return null;
    }
}

三、Spring Security的配置

WebSecurityConfig.java
package com.springsecurityjwt.springsecurityjwt.config;

import com.springsecurityjwt.springsecurityjwt.filter.JWTLoginFilter;
import com.springsecurityjwt.springsecurityjwt.filter.JwtAuthenticationFilter;
import com.springsecurityjwt.springsecurityjwt.filter.JwtAuthenticationTwoFilter;
import com.springsecurityjwt.springsecurityjwt.handler.Http401AuthenticationEntryPoint;
import com.springsecurityjwt.springsecurityjwt.service.impl.CustomAuthenticationProvider;
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.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.annotation.web.configurers.LogoutConfigurer;
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.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @Author zhaomengxia
 * @create 2019/9/16 14:00
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{


    @Bean
    public JwtAuthenticationTwoFilter jwtAuthenticationTwoFilter(){
        return new JwtAuthenticationTwoFilter();
    }

    private static final String[] AUTH_WHITELIST = {
            // -- register url
            "/user/signup",
            "/user/addTask",
            // -- swagger ui
            "/v2/api-docs",
            "/swagger-resources",
            "/swagger-resources/**",
            "/configuration/ui",
            "/configuration/security",
            "/swagger-ui.html",
            "/user/login",
            "/login",
            "/webjars/**"
            // other public endpoints of your API may be appended to this array
    };

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        LogoutConfigurer<HttpSecurity> httpSecurityLogoutConfigurer=http.cors().and()
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(AUTH_WHITELIST).permitAll()
                .anyRequest().authenticated()
                .and()
                .exceptionHandling()
                   .authenticationEntryPoint(new Http401AuthenticationEntryPoint("Basic realm=\"MyApp\""))
                .and()
//                .addFilter(new JWTLoginFilter(authenticationManager()))
//                .addFilter(new JwtAuthenticationFilter(authenticationManager()))
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login")
                .permitAll();

        http.addFilterBefore(jwtAuthenticationTwoFilter(),UsernamePasswordAuthenticationFilter.class);
    }


    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(new CustomAuthenticationProvider(userDetailsService,bCryptPasswordEncoder));
    }
}
1.添加JwtTokenProvider.java和JwtAuthenticationTwoFilter.java并继承OncePerRequestFilter类
2.启动SpringsecurityjwtApplication.java类
3.浏览器访问http://localhost:18081/swagger-ui.html#
4.在swagger  注册一个用户信息
5.在swagger 调用http://localhost:18081/user/login这个接口登录,Authorization随便填,用户名密码输入正确即可登陆成功,在Response Body下面即可获得token(accessToken对应的值)

6.在swagger 将5拿到的token用来测试接口http://localhost:18081/user/findByUserName,Authorization放token, 接着userName对应输入刚第4步注册的用户名,即可测试token可用不可用

第5步登录

第6步测试

源代码及数据

https://github.com/zhaomengxia/springboot_mybatis.git 

项目中该模块的位置

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值