SpringSecurity integrates the Centralized version of SpringBoot

Technology selection

  • SpringBoot 2.1.3
  • SpringSecurity
  • MySQL 5.7
  • Mybatis
  • JSP

Preliminary integration certification first edition

Create the project and import the JAR package

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.3. RELEASE</version>
    <relativePath/>
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
Copy the code

Writing controller

package com.weiwei.xu.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping(value = "/product")
public class ProductController {

    @RequestMapping(value = "/findAll")
    @ResponseBody
    public String findAll(a){
        return "success"; }}Copy the code

Writing a startup class

package com.weiwei.xu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootSecurityApplication {
    public static void main(String[] args) { SpringApplication.run(SpringBootSecurityApplication.class, args); }}Copy the code

test

  • Input: http://localhost:8080/product/findAll.

Add the SpringSecurity jar package

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Copy the code

Restart the test

  • SpringBoot provides a default configuration for SpringSecurity. By default, all resources must be authenticated before they can be accessed.

  • SpringBoot already provides a default user name, user, and the password is generated randomly at project startup, as shown below:

  • After authentication, access to processor resources can continue:

The project address

  • Initial integration certification version 1 address

Preliminary integration certification second edition

instructions

  • SpringBoot is not officially recommended to use JSP in SpringBoot, but SpringBoot can use JSP as a template engine, this project uses JSP as a front-end display page.

Import the Tomcat startup plug-in JAR package of SpringBoot

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
	<groupId>org.apache.tomcat.embed</groupId>
	<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
Copy the code

Add static resources such as JSP pages

  • If there is no webApp directory in the SRC /main directory, create the WebApp directory and manually change the Java project to a Web project.

  • If the WebApp directory looks something like the one below, you can use it.

  • Import static resources, note that web-INF is not used.

Provides a configuration class for SpringSecurity

package com.weiwei.xu.config;

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;

/** * SpringSecurity configuration class **@author weiwei.xu
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {


    /** * The source of the authentication user [memory or database] **@param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("user").password("{noop}123456").roles("USER");
    }


    /** * SpringSecurity information **@param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // Release static resources, specify resource blocking rules, customize authentication pages, exit authentication configuration, and CSRF configuration
        http.authorizeRequests().antMatchers("/login.jsp"."/failer.jsp"."/css/**"."/img/**"."/pages/**").permitAll()
                .antMatchers("/ * *").access("hasAnyRole('USER','ADMIN')")
                .anyRequest()
                .authenticated()
                .and()
                .formLogin().loginPage("/login.jsp").loginProcessingUrl("/login").successForwardUrl("/index.jsp").failureForwardUrl("/failer.jsp").permitAll()
                .and()
                .logout().logoutUrl("/logout").logoutSuccessUrl("/login.jsp")
                .invalidateHttpSession(true) / / clear the session.permitAll(); }}Copy the code

Modifying the startup class

package com.weiwei.xu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

/** * Start class **@author weiwei.xu
 */
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
public class SpringBootSecurityApplication extends SpringBootServletInitializer {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootSecurityApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        returnapplication.sources(SpringBootSecurityApplication.class); }}Copy the code

Configure the view resolver

  • application.yml
server:
  port: 8080

Configure the view resolver
spring:
  mvc:
    view:
      prefix: /pages/
      suffix: .jsp

Copy the code

test

Customize the authentication page

Authentication success page

The project address

  • Preliminary integration certification second edition address

Preliminary Integration Certification Third Edition (Database Certification)

The preparatory work

/* Navicat Premium Data Transfer Source: 192.168.1.57 Source Server Type: MySQL Source Server Version: 50729 Source Host: 192.168.1.57:33060 Source Schema: Security Target Server Type: MySQL Target Server Version: 50729 File Encoding : 65001 Date: 26/04/2020 11:18:36 */

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for persistent_logins
-- ----------------------------
DROP TABLE IF EXISTS `persistent_logins`;
CREATE TABLE `persistent_logins`  (
  `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL.`series` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL.`token` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL.`last_used` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0),
  PRIMARY KEY (`series`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of persistent_logins
-- ----------------------------
INSERT INTO `persistent_logins` VALUES ('xiaoming'.'93OEeplI7zAKwc0BqVnW+Q=='.'e4JvptA5h8fR+FFgmJSL9g=='.'the 2020-04-24 10:23:57');
INSERT INTO `persistent_logins` VALUES ('xiaoming'.'CmWGQ/rmb7lXAx7dHiEVGw=='.'OvsaKtaC046whNL56Y4MqA=='.'the 2020-04-24 10:30:29');
INSERT INTO `persistent_logins` VALUES ('xiaoming'.'JJqJ5vJ/7IqMLxj/JpLDRg=='.'vIfFHeD6fYSV8oCHMdty7g=='.'the 2020-04-24 10:42:48');
INSERT INTO `persistent_logins` VALUES ('xiaoming'.'T9NMx6AiG5J3RNSM841juw=='.'v67JBqXg7AkhLtH++LISNw=='.'the 2020-04-24 10:35:57');

-- ----------------------------
-- Table structure for sys_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'number'.`permission_name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Menu name'.`permission_url` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Menu address'.`parent_id` int(11) NOT NULL DEFAULT 0 COMMENT 'Parent menu ID',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'number'.`role_name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Role Name'.`role_desc` varchar(60) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Character Description',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES (6.'ROLE_USER'.'Ordinary user');
INSERT INTO `sys_role` VALUES (7.'ROLE_ADMIN'.'System Administrator');

-- ----------------------------
-- Table structure for sys_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_permission`;
CREATE TABLE `sys_role_permission`  (
  `rid` int(11) NOT NULL COMMENT 'Role Number'.`pid` int(11) NOT NULL COMMENT 'Permission Number',
  PRIMARY KEY (`rid`.`pid`) USING BTREE,
  INDEX `FK_Reference_12`(`pid`) USING BTREE,
  CONSTRAINT `FK_Reference_11` FOREIGN KEY (`rid`) REFERENCES `sys_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `FK_Reference_12` FOREIGN KEY (`pid`) REFERENCES `sys_permission` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'User name'.`password` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'password'.`status` int(1) NULL DEFAULT 1 COMMENT '1 on 0 off ',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (4.'admin'.'$2a$10$ynlaufZM048G5jsp98seeuvkAXNCVD5RFEudlrW.xiNihU.2Tjm9W'.1);
INSERT INTO `sys_user` VALUES (5.'xiaoming'.'$2a$10$ynlaufZM048G5jsp98seeuvkAXNCVD5RFEudlrW.xiNihU.2Tjm9W'.1);

-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role`  (
  `uid` int(11) NOT NULL COMMENT 'User number'.`rid` int(11) NOT NULL COMMENT 'Role Number',
  PRIMARY KEY (`uid`.`rid`) USING BTREE,
  INDEX `FK_Reference_10`(`rid`) USING BTREE,
  CONSTRAINT `FK_Reference_10` FOREIGN KEY (`rid`) REFERENCES `sys_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `FK_Reference_9` FOREIGN KEY (`uid`) REFERENCES `sys_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES (5.6);
INSERT INTO `sys_user_role` VALUES (4.7);

SET FOREIGN_KEY_CHECKS = 1;
Copy the code

Import jar packages related to database operations

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.2</version>
</dependency>
Copy the code

Add database operations to the configuration file

  • application.yml
server:
  port: 8080

Configure the view resolver
spring:
  mvc:
    view:
      prefix: /pages/
      suffix: .jsp
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: JDBC: mysql: / / 192.168.1.57:33060 / security
    username: root
    password: 123456

mybatis:
  type-aliases-package: com.weiwei.xu.domain
  mapper-locations:  classpath:/com/weiwei/xu/mapper/*.mapper.xml
Copy the code

Writing entity classes

  • SysUser.java
package com.weiwei.xu.domain;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/** * System user **@author weiwei.xu
 */
public class SysUser implements UserDetails {

    private Integer id;

    private String username;

    private String password;

    private Integer status;

    private Set<SysRole> roles = new HashSet<>();

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

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

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

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


    public Integer getId(a) {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public String getUsername(a) {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String getPassword(a) {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getStatus(a) {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public Set<SysRole> getRoles(a) {
        return roles;
    }

    public void setRoles(Set<SysRole> roles) {
        this.roles = roles;
    }

    @JsonIgnore
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        returnroles; }}Copy the code
  • SysRole.java
package com.weiwei.xu.domain;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;

/** * System role **@author weiwei.xu
 */
public class SysRole implements GrantedAuthority {

    private Integer id;

    private String roleName;

    private String roleDesc;

    public Integer getId(a) {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getRoleName(a) {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getRoleDesc(a) {
        return roleDesc;
    }

    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }

    @JsonIgnore
    @Override
    public String getAuthority(a) {
        returnroleName; }}Copy the code

Write a Mapper interface

  • SysUserMapper.java
package com.weiwei.xu.mapper;

import com.weiwei.xu.domain.SysUser;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import java.util.Set;

public interface SysUserMapper {

    @Select("select * from sys_user where username = #{username} ")
    @Results({
            @Result(id = true, property = "id", column = "id"),
            @Result(property = "roles", column = "id", javaType = Set.class,
                    many = @Many(select = "com.weiwei.xu.mapper.SysRoleMapper.findByUid"))})SysUser findByUsername(String username);

}
Copy the code
  • SysRoleMapper.java
package com.weiwei.xu.mapper;

import com.weiwei.xu.domain.SysRole;
import org.apache.ibatis.annotations.Select;

import java.util.Set;

public interface SysRoleMapper {

    /** * Query the user role ** based on the uid@param uid
     * @return* /
    @Select("SELECT r.id, r.role_name roleName, r.role_desc roleDesc " +
            "FROM sys_role r, sys_user_role ur " +
            "WHERE r.id=ur.rid AND ur.uid=#{uid}")
    Set<SysRole> findByUid(Integer uid);

}
Copy the code

Write business layer interfaces and implementation classes

  • SysUserService.java
package com.weiwei.xu.service;

import org.springframework.security.core.userdetails.UserDetailsService;

public interface SysUserService extends UserDetailsService {}Copy the code
  • SysUserServiceImpl.java
package com.weiwei.xu.service.impl;

import com.weiwei.xu.mapper.SysUserMapper;
import com.weiwei.xu.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class SysUserServiceImpl implements SysUserService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        returnsysUserMapper.findByUsername(username); }}Copy the code

Modify the Configuration class for SpringSecurity

package com.weiwei.xu.config;

import com.weiwei.xu.service.SysUserService;
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.crypto.bcrypt.BCryptPasswordEncoder;

/** * SpringSecurity configuration class **@author weiwei.xu
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private SysUserService sysUserService;

    @Bean
    public BCryptPasswordEncoder passwordEncoder(a) {
        return new BCryptPasswordEncoder();
    }

    /** * The source of the authentication user [memory or database] **@param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(sysUserService).passwordEncoder(passwordEncoder());
    }


    /** * SpringSecurity information **@param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // Release static resources, specify resource blocking rules, customize authentication pages, exit authentication configuration, and CSRF configuration
        http.authorizeRequests().antMatchers("/login.jsp"."/failer.jsp"."/css/**"."/img/**"."/pages/**").permitAll()
                .antMatchers("/ * *").access("hasAnyRole('USER','ADMIN')")
                .anyRequest()
                .authenticated()
                .and()
                .formLogin().loginPage("/login.jsp").loginProcessingUrl("/login").successForwardUrl("/index.jsp").failureForwardUrl("/failer.jsp").permitAll()
                .and()
                .logout().logoutUrl("/logout").logoutSuccessUrl("/login.jsp")
                .invalidateHttpSession(true) / / clear the session.permitAll(); }}Copy the code
  • Enable method-level authorization annotations on startup classes:
package com.weiwei.xu;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;

/** * Start class **@author weiwei.xu
 */
@MapperScan("com.weiwei.xu.mapper")
@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SpringBootSecurityApplication extends SpringBootServletInitializer {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootSecurityApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        returnapplication.sources(SpringBootSecurityApplication.class); }}Copy the code
  • Add annotations to product handlers:
package com.weiwei.xu.controller;

import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping(value = "/product")
public class ProductController {

    @RequestMapping(value = "/findAll")
    @Secured({"ROLE_PRODUCT"."ROLE_ADMIN"})
    public String findAll(a){
        return "product-list"; }}Copy the code

Handle 403 exception and other exceptions

package com.weiwei.xu.exception;

import org.springframework.security.acls.model.NotFoundException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import java.nio.file.AccessDeniedException;

@ControllerAdvice
public class SecurityExceptionHandler {

    @ExceptionHandler(value = AccessDeniedException.class)
    public String handle403Exception(a){
        return "redirect:403";
    }

    @ExceptionHandler(value = RuntimeException.class)
    public String handle500Exception(a){
        return "redirect:500";
    }

    @ExceptionHandler(value = NotFoundException.class)
    public String handle404Exception(a){
        return "redirect:404"; }}Copy the code

The project address

  • Preliminary integration certification third edition

SpringSecurity integrates the Distributed version of SpringBoot

Distributed authentication concept description

  • Distributed authentication, also known as single sign-on, or SSO for short, refers to a multi-application project in which users only need to log in once to access all trusted applications.

Distributed Authentication Flow Chart

  • In distributed projects, each server has its own separate session, and these sessions cannot share resources directly. Therefore, sessions are usually not a solution for single sign-on (SSO).
  • The most reasonable single sign-on scheme flow is shown in the following figure:

  • To sum up, single sign-on should be realized in two aspects:
    • (1) User authentication: The user initiates an authentication request to the authentication server, and the authentication server returns a successful token to the user. This is mainly done in the authentication server, which has only one authentication server.
    • ② Identity verification: When a user carries a token to access another server, the authentication of the token is performed on the other server. The authentication is mainly performed on the resource server. There may be multiple resource servers.

JWT is introduced

The concept that

  • From the distributed authentication process, IT is not difficult for me to find that token plays the most critical role in this process. Whether token is secure or not is directly related to the robustness of the system. Here, WE choose JWT to achieve the generation and verification of token.

  • JWT, full JSON Web Token, official website address, is an excellent distributed authentication solution. Tokens can be generated or parsed and verified.

  • The token generated by JWT consists of the following three parts:

    • Header: mainly set some standard information, the signature part of the encoding format is declared in the header.
    • Payload: The part of the token that stores valid information, such as user name, user role, expiration time, etc., but do not put the password, it will be disclosed!
    • Signature: After encoding the header and payload in Base64 respectively, use “. Join, add salt, and then code using the type of code declared in the header to get the signature.

Security analysis of tokens generated by JWT

  • From the perspective of the composition of the token generated by JWT, to avoid the forgery of the token, it mainly depends on the signature part, and the signature part is composed of three parts, including the base code of the head and payload, which is almost transparent and has no security at all. Therefore, the burden of protecting the security of the token ultimately falls on the adding of salt.
  • If the salt used to generate the token is the same as the salt used to parse the token, the consequences will be terrible. Everyone can use the salt to parse the token, to forge the token.
  • In this case, asymmetric encryption is required to encrypt the salt to ensure the security effect of inconsistent salt used to generate the token and verify the token.

Asymmetric secret RSA introduction

  • Basic principle: Generate two keys at the same time: the private key and the public key. The private key is hidden and stored, and the public key can be sent to the trusted client.

    • Private key encryption, can be decrypted only with a private key or public key.
    • Public key encryption, private key can be decrypted.
  • Advantages: Safety.

  • Disadvantages: the algorithm is time-consuming, for security, acceptable.

  • History: Three mathematicians, Rivest, Shamir and Adleman, designed an algorithm that allows asymmetric encryption. The algorithm goes by their initials: RSA.

Analysis of SpringSecurity+JWT+RSA distributed authentication

SpringSecurity implements its functions primarily through filters, so we need to find the filters that SpringSecurity implements to authenticate and verify identities.

Review the centralized certification process

  • User authentication:

    • Use UsernamePasswordAuthenticationFilter filter attemptAuthentication method of authentication function, the filter successfulAuthentication method in the parent class of implement certification after a successful operation.
  • Identity verification:

    • The doFilterInternal BasicAuthenticationFilter filter method is used to verify login, to decide whether to enter the subsequent filter.

Distributed Authentication Process

  • User authentication:

    • Because, distributed project, most of the architectural design, the separation of front and back side we are to meet acceptable asynchronous POST authentication request parameters, the need to modify the UsernamePasswordAuthenticationFilter attemptAuthentication methods of filters, Make it able to accept the request body. In addition, the default successfulAuthentication method puts the user’s information directly into the session after authentication. Now we need to modify this method to generate a token after authentication and return it to the user.
  • Identity authentication:

    • In original BasicAuthenticationFilter filter doFilterInternal method to check whether the user login, is to see if there is a user session information, we need to modify for authenticating the user carry a token is legal, and parse out the user information, Give it to SpringSecurity so that subsequent authorization functions can be used properly.

SpringSecurity+JWT+RSA distributed implementation

Import the corresponding JAR package

<properties>
        <java.version>1.8</java.version>
        <! -- swagger -->
        <io.springfox.version>2.9.2</io.springfox.version>
        <io.swagger.version>1.5.21</io.swagger.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-configuration-processor</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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>
            <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-api</artifactId>
            <version>0.10.7</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.10.7</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.10.7</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>4.6.4</version>
        </dependency>
        <! -- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>
        <! -- Swagger -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${io.springfox.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>io.swagger</groupId>
                    <artifactId>swagger-annotations</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>io.swagger</groupId>
                    <artifactId>swagger-models</artifactId>
                </exclusion>
                <exclusion>
                    <artifactId>mapstruct</artifactId>
                    <groupId>org.mapstruct</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${io.springfox.version}</version>
        </dependency>
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>${io.swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
            <version>${io.swagger.version}</version>
        </dependency>
    </dependencies>

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

Utility class

  • RsaUtils.java
package com.weiwei.xu.utils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/** * RSA algorithm **@author weiwei.xu
 */
public class RsaUtils {

    private static final int DEFAULT_KEY_SIZE = 2048;

    /** * Read public key ** from file@paramFilename Specifies the path to save the public key. The value is relative to classpath *@returnPublic key Object@throws Exception
     */
    public static PublicKey getPublicKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPublicKey(bytes);
    }

    /** * Read the key from the file **@paramFilename Specifies the path to save the private key, relative to classpath *@returnPrivate key object *@throws Exception
     */
    public static PrivateKey getPrivateKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPrivateKey(bytes);
    }

    /** * Get public key **@paramBytes Bytes of the public key *@return
     * @throws Exception
     */
    private static PublicKey getPublicKey(byte[] bytes) throws Exception {
        bytes = Base64.getDecoder().decode(bytes);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePublic(spec);
    }

    /** * get the key **@paramBytes Bytes of the private key *@return
     * @throws Exception
     */
    private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        bytes = Base64.getDecoder().decode(bytes);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePrivate(spec);
    }

    /** * Live the RSA public and private keys according to the ciphertext, and write the specified file **@paramPublicKeyFilename Public key file path *@paramPrivateKeyFilename Private key file path *@paramSecret Indicates the ciphertext for generating the key */
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret, int keySize) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // Get the public key and write
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        publicKeyBytes = Base64.getEncoder().encode(publicKeyBytes);
        writeFile(publicKeyFilename, publicKeyBytes);
        // Get the private key and write
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        privateKeyBytes = Base64.getEncoder().encode(privateKeyBytes);
        writeFile(privateKeyFilename, privateKeyBytes);
    }

    private static byte[] readFile(String fileName) throws Exception {
        return Files.readAllBytes(new File(fileName).toPath());
    }

    private static void writeFile(String destPath, byte[] bytes) throws IOException {
        File dest = new File(destPath);
        if(! dest.getParentFile().exists()) { dest.getParentFile().mkdirs(); }if (!dest.exists()) {
            dest.createNewFile();
        }
        Files.write(dest.toPath(), bytes);
    }
}
Copy the code
  • JwtUtils.java
package com.weiwei.xu.utils;


import cn.hutool.json.JSONUtil;
import com.weiwei.xu.common.Payload;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.sql.Date;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Base64;
import java.util.UUID;

/** * Generate token and verify token related methods **@author: weiwei.xu
 */
public class JwtUtils {

    private static final String JWT_PAYLOAD_USER_KEY = "user";

    /** * Private key encryption token **@paramUserInfo Payload data *@paramPrivateKey private key *@paramExpire Expiration time, in minutes *@return JWT
     */
    public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) {
        return Jwts.builder()
                .claim(JWT_PAYLOAD_USER_KEY, JSONUtil.parseObj(userInfo).toStringPretty())
                .setId(createJTI())
                .setExpiration(Date.from(LocalDateTime.now().plusMinutes(expire).atZone(ZoneId.systemDefault()).toInstant()))
                .signWith(privateKey, SignatureAlgorithm.RS256)
                .compact();
    }

    /** * Private key encryption token **@paramUserInfo Payload data *@paramPrivateKey private key *@paramExpire expiration time, in seconds *@return JWT
     */
    public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) {
        return Jwts.builder()
                .claim(JWT_PAYLOAD_USER_KEY, JSONUtil.parseObj(userInfo).toStringPretty())
                .setId(createJTI())
                .setExpiration(Date.from(LocalDateTime.now().plusSeconds(expire).atZone(ZoneId.systemDefault()).toInstant()))
                .signWith(privateKey, SignatureAlgorithm.RS256)
                .compact();
    }

    /** * Public key parse token **@paramToken Indicates the token * in the user request@paramPublicKey public key *@return Jws<Claims>
     */
    private static Jws<Claims> parserToken(String token, PublicKey publicKey) {
        return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
    }

    private static String createJTI(a) {
        return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
    }

    /** * Get user information from token **@paramToken Token * in the user request@paramPublicKey public key *@returnUser information */
    public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) {
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        Payload<T> claims = new Payload<>();
        claims.setId(body.getId());
        claims.setUserInfo(JSONUtil.toBean(body.get(JWT_PAYLOAD_USER_KEY).toString(), userType));
        claims.setExpiration(body.getExpiration());
        return claims;
    }

    /** * Get the payload information in token **@paramToken Token * in the user request@paramPublicKey public key *@returnUser information */
    public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey) {
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        Payload<T> claims = new Payload<>();
        claims.setId(body.getId());
        claims.setExpiration(body.getExpiration());
        returnclaims; }}Copy the code
  • Payload.java
package com.weiwei.xu.common;

import lombok.*;

import java.util.Date;

/ * * *@authorWeiwei. xu * in order to facilitate the later acquisition of user information in the token, the payload part of the token is separately encapsulated into an object */
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
@ToString
public class Payload<T> {
    private String id;
    private T userInfo;
    private Date expiration;
}
Copy the code

SpringBoot configuration file

  • application.yml
server:
  port: 8080
  servlet:
    context-path: /security

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: JDBC: mysql: / / 192.168.1.57:33060 / security? useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=t rue
    username: root
    password: 123456


mybatis:
  mapper-locations: classpath:com/weiwei/xu/mapper/*.mapper.xml
  type-aliases-package: com.weiwei.xu.domain
  configuration:
    map-underscore-to-camel-case: true

rsa:
  key:
    pubKeyFile: D:/auth_key/id_key_rsa.pub
    priKeyFile: D:/auth_key/id_key_rsa

Copy the code

The configuration class

  • RsaKeyProperties.java
package com.weiwei.xu.config;

import com.weiwei.xu.utils.RsaUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.security.PrivateKey;
import java.security.PublicKey;

/ * * *@author weiwei.xu
 */
@Configuration
@ConfigurationProperties(value = "rsa.key")
public class RsaKeyProperties {

    private String pubKeyFile;

    private String priKeyFile;

    private PublicKey publicKey;

    private PrivateKey privateKey;

    public String getPubKeyFile(a) {
        return pubKeyFile;
    }

    public void setPubKeyFile(String pubKeyFile) {
        this.pubKeyFile = pubKeyFile;
    }

    public String getPriKeyFile(a) {
        return priKeyFile;
    }

    public void setPriKeyFile(String priKeyFile) {
        this.priKeyFile = priKeyFile;
    }

    public PublicKey getPublicKey(a) {
        return publicKey;
    }

    public void setPublicKey(PublicKey publicKey) {
        this.publicKey = publicKey;
    }

    public PrivateKey getPrivateKey(a) {
        return privateKey;
    }

    public void setPrivateKey(PrivateKey privateKey) {
        this.privateKey = privateKey;
    }

    @PostConstruct
    public void createResKey(a) throws Exception { publicKey = RsaUtils.getPublicKey(pubKeyFile); privateKey = RsaUtils.getPrivateKey(priKeyFile); }}Copy the code
  • SpringSecurityConfig.java
package com.weiwei.xu.config;

import com.weiwei.xu.filter.JwtTokenFilter;
import com.weiwei.xu.filter.JwtVerifyFilter;
import com.weiwei.xu.service.SysUserService;
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.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/** * SpringSecurity configuration class **@author weiwei.xu
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    private SysUserService sysUserService;

    @Bean
    public BCryptPasswordEncoder passwordEncoder(a) {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    private RsaKeyProperties rsaKeyProperties;


    /** * Configure authentication source **@param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(sysUserService).passwordEncoder(passwordEncoder());
    }

    /** * Configure the filter request **@param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {


        http.   // Close cross-domain site requests
                cors().and().csrf().disable().authorizeRequests()
                / / swagger document
                .antMatchers("/swagger-ui.html/**").permitAll()
                .antMatchers("/swagger-resources/**").permitAll()
                .antMatchers("/webjars/**").permitAll()
                .antMatchers("/*/api-docs").permitAll()
                / / file
                .antMatchers("/avatar/**").permitAll()
                .antMatchers("/file/**").permitAll()
                // Alibaba druid
                .antMatchers("/druid/**").permitAll()
                .anyRequest().authenticated()
                .and()
                // Customize the authentication filter
                .addFilter(new JwtTokenFilter(authenticationManager(), rsaKeyProperties))
                // Customize URL filters
                .addFilter(newJwtVerifyFilter(authenticationManager(), rsaKeyProperties)) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) ; }}Copy the code
  • SwaggerConfig.java
package com.weiwei.xu.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

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

/**
 * SwaggerConfig
 *
 * @author weiwei.xu
 */
@Configuration
@EnableSwagger2
public class SwaggerConfig {
    static List<Parameter> parameters;

    static {
        /** * This is to add header information */ when we test the interface with swagger
        parameters = new ArrayList<>();
        ParameterBuilder tokenPar = new ParameterBuilder();
        tokenPar.name("Authorization").description("Swagger test with (mock token passed in) optional header").modelRef(new ModelRef("string")).parameterType("header").required(false);
        parameters.add(tokenPar.build());
    }

    @Bean
    public Docket docket(a) {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
// .apis(RequestHandlerSelectors.basePackage("com.weiwei.xu.controller.ProductController"))
                .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
                .paths(PathSelectors.ant("/ * *"))
                .build()
                .globalOperationParameters(parameters)
                .enable(true);
    }


    private ApiInfo apiInfo(a) {
        return new ApiInfoBuilder()
                .title("SpringBoot integration with SpringSecurity Distributed version")
                .termsOfServiceUrl("")
                .version("1.0") .build(); }}Copy the code

Entity class

  • SysUser.java
package com.weiwei.xu.domain;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.*;

/** * System user **@author weiwei.xu
 */
public class SysUser implements UserDetails {

    private Integer id;

    private String username;

    private String password;

    private Integer status;

    private List<SysRole> roles = new ArrayList<>();

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

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

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

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


    public Integer getId(a) {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public String getUsername(a) {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String getPassword(a) {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getStatus(a) {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public List<SysRole> getRoles(a) {
        return roles;
    }

    public void setRoles(List<SysRole> roles) {
        this.roles = roles;
    }

    @JsonIgnore
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        returnroles; }}Copy the code
  • SysRole.java
package com.weiwei.xu.domain;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;

/** * System role **@author weiwei.xu
 */
public class SysRole implements GrantedAuthority {

    private Integer id;

    private String roleName;

    private String roleDesc;

    public Integer getId(a) {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getRoleName(a) {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getRoleDesc(a) {
        return roleDesc;
    }

    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }

    @JsonIgnore
    @Override
    public String getAuthority(a) {
        returnroleName; }}Copy the code

Mapper interfaces

  • SysUserMapper.java
package com.weiwei.xu.mapper;

import com.weiwei.xu.domain.SysUser;
import org.apache.ibatis.annotations.*;

import java.util.List;
@Mapper
public interface SysUserMapper {

    @Select("select * from sys_user where username = #{username} ")
    @Results({
            @Result(id = true, property = "id", column = "id"),
            @Result(property = "roles", column = "id", javaType = List.class,
                    many = @Many(select = "com.weiwei.xu.mapper.SysRoleMapper.findByUid"))})SysUser findByUsername(String username);

}
Copy the code
  • SysRoleMapper.java
package com.weiwei.xu.mapper;

import com.weiwei.xu.domain.SysRole;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.Set;
@Mapper
public interface SysRoleMapper {

    /** * Query the user role ** based on the uid@param uid
     * @return* /
    @Select("SELECT r.id, r.role_name roleName, r.role_desc roleDesc " +
            "FROM sys_role r, sys_user_role ur " +
            "WHERE r.id=ur.rid AND ur.uid=#{uid}")
    Set<SysRole> findByUid(Integer uid);

}

Copy the code

Service layer interface and implementation class

  • SysUserService.java
package com.weiwei.xu.service;

import org.springframework.security.core.userdetails.UserDetailsService;

public interface SysUserService extends UserDetailsService {}Copy the code
  • SysUserServiceImpl.java
package com.weiwei.xu.service.impl;

import com.weiwei.xu.mapper.SysUserMapper;
import com.weiwei.xu.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class SysUserServiceImpl implements SysUserService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        returnsysUserMapper.findByUsername(username); }}Copy the code

The controller

  • ProductController.java
package com.weiwei.xu.controller;

import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@RestController
@RequestMapping(value = "/product")
public class ProductController {

    @GetMapping(value = "/findAll")
    @Secured({"ROLE_PRODUCT"})
    public Map<String, Object> findAll(a) {
        Map<String, Object> map = new ConcurrentHashMap<>();
        map.put("aa"."bb");
        returnmap; }}Copy the code

Jwt-related filters

  • JwtTokenFilter.java
package com.weiwei.xu.filter;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.weiwei.xu.config.RsaKeyProperties;
import com.weiwei.xu.domain.SysRole;
import com.weiwei.xu.domain.SysUser;
import com.weiwei.xu.utils.JwtUtils;
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 javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/** * JWT token Filter **@author weiwei.xu
 */
public class JwtTokenFilter extends UsernamePasswordAuthenticationFilter {

    private RsaKeyProperties rsaKeyProperties;

    private AuthenticationManager authenticationManager;

    public JwtTokenFilter(AuthenticationManager authenticationManager, RsaKeyProperties rsaKeyProperties) {
        this.rsaKeyProperties = rsaKeyProperties;
        this.authenticationManager = authenticationManager;
    }

    /** * The method of trying to authenticate **@param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

        try {
            SysUser sysUser = new ObjectMapper().readValue(request.getInputStream(), SysUser.class);

            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(sysUser.getUsername(), sysUser.getPassword());

            return authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        } catch (Exception e) {
            try {
                // If the authentication fails, user-defined JSON exception information is provided
                response.setContentType("application/json; charset=utf-8");
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                PrintWriter writer = response.getWriter();
                Map<String, Object> resultMap = new ConcurrentHashMap<>();
                resultMap.put("code", HttpServletResponse.SC_FORBIDDEN);
                resultMap.put("msg"."Wrong username or password");
                writer.write(new ObjectMapper().writeValueAsString(resultMap));
                writer.flush();
                writer.close();
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            return null; }}/** * After successful authentication, the token is generated and returned to the front end **@param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        String name = authResult.getName();
        SysUser sysUser = new SysUser();
        sysUser.setUsername(name);

        sysUser.setRoles((List<SysRole>) authResult.getAuthorities());

        String token = JwtUtils.generateTokenExpireInMinutes(sysUser, rsaKeyProperties.getPrivateKey(), 24 * 60);

        // Respond the token to the client
        response.addHeader("Authorization"."Bearer " + token);

        response.setContentType("application/json; charset=utf-8");
        response.setStatus(HttpServletResponse.SC_OK);
        PrintWriter writer = response.getWriter();
        Map<String, Object> resultMap = new ConcurrentHashMap<>();
        resultMap.put("code", HttpServletResponse.SC_OK);
        resultMap.put("msg"."Login successful");
        writer.write(newObjectMapper().writeValueAsString(resultMap)); writer.flush(); writer.close(); }}Copy the code
  • JwtVerifyFilter.java
package com.weiwei.xu.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.weiwei.xu.common.Payload;
import com.weiwei.xu.config.RsaKeyProperties;
import com.weiwei.xu.domain.SysUser;
import com.weiwei.xu.utils.JwtUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
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.io.PrintWriter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/ * * *@author weiwei.xu
 */
public class JwtVerifyFilter extends BasicAuthenticationFilter {

    private RsaKeyProperties rsaKeyProperties;

    public JwtVerifyFilter(AuthenticationManager authenticationManager, RsaKeyProperties rsaKeyProperties) {
        super(authenticationManager);
        this.rsaKeyProperties = rsaKeyProperties;
    }


    /** * Filter requests **@param request
     * @param response
     * @param chain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public 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);
// try {
// response.setContentType("application/json; charset=utf-8");
// response.setStatus(HttpServletResponse.SC_FORBIDDEN);
// PrintWriter writer = response.getWriter();
// Map
      
        resultMap = new ConcurrentHashMap<>();
      ,>
// resultMap.put("code", HttpServletResponse.SC_FORBIDDEN);
// resultmap. put(" MSG ", "please login ");
// writer.write(new ObjectMapper().writeValueAsString(resultMap));
// writer.flush();
// writer.close();
// } catch (IOException e) {
// e.printStackTrace();
/ /}

            return;
        } else {
            String token = header.replace("Bearer "."");
            Payload<SysUser> sysUserPayload = null;
            try {
                sysUserPayload = JwtUtils.getInfoFromToken(token, rsaKeyProperties.getPublicKey(), SysUser.class);
            } catch (Exception e) {

                try {
                    // If the authentication fails, user-defined JSON exception information is provided
                    response.setContentType("application/json; charset=utf-8");
                    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                    PrintWriter writer = response.getWriter();
                    Map<String, Object> resultMap = new ConcurrentHashMap<>();
                    resultMap.put("code", HttpServletResponse.SC_FORBIDDEN);
                    resultMap.put("msg"."Token resolution error");
                    writer.write(new ObjectMapper().writeValueAsString(resultMap));
                    writer.flush();
                    writer.close();
                } catch (Exception exception) {
                    exception.printStackTrace();
                }
            }


            SysUser sysUser = sysUserPayload.getUserInfo();

            if (null! = sysUser) { UsernamePasswordAuthenticationToken authentication =newUsernamePasswordAuthenticationToken(sysUser.getUsername(), sysUser.getPassword(), sysUser.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); chain.doFilter(request, response); }}}}Copy the code

The project address

  • SpringSecurity integrates the Distributed version of SpringBoot