Spring Security入门教程,springboot整合Spring Security

Spring Security是Spring官方推荐的认证、授权框架,功能相比Apache Shiro功能更丰富也更强大,但是使用起来更麻烦。

如果使用过Apache Shiro,学习Spring Security会比较简单一点,两种框架有很多相似的地方。

目录

一、准备工作

创建springboot项目

pom.xml

application.yml

二、创建相关的类

UserDetailsService

SecurityConfig.java

SystemProperties.java

MybatisPlusConfig.java

三、完成登录接口

创建数据库实体类

创建持久层接口

创建登录DTO对象

创建控制器类

创建业务层类

自定义登录成功处理器


一、准备工作

创建springboot项目

首先,通过IntelliJ IDEA创建一个springboot项目,项目名为springboot-springsecurity,在pom.xml中添加相关依赖。

pom.xml



    4.0.0

    
        org.springframework.boot
        spring-boot-starter-parent
        2.3.4.RELEASE
        
    

    com.example
    springboot-springsecurity
    0.0.1-SNAPSHOT

    
        1.8
        0.9.1
        8.0.28
        1.1.21
        1.18.22
        2.2.2
        3.5.1
    

    
        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            org.springframework.boot
            spring-boot-starter-validation
        

        
        
            org.springframework.boot
            spring-boot-starter-security
        

        
        
            mysql
            mysql-connector-java
            ${mysql.version}
        

        
        
            com.alibaba
            druid
            ${druid.version}
        

        
        
            org.projectlombok
            lombok
            ${lombok.version}
        

        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            ${mybatis.version}
        

        
        
            com.baomidou
            mybatis-plus-boot-starter
            ${mybatis-plus.version}
        

        
        
            io.jsonwebtoken
            jjwt
            ${jjwt.version}
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

application.yml

server:
  port: 8080
  servlet:
    context-path: /

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/spring_security
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

mybatis-plus:
  mapper-locations: classpath:mapper/*Mapper.xml

logging:
  level:
    springfox: error
    com.example.security: debug


system:
  login-page: /login.html
  login-url: /user/login
  index-page: /index.html
  logout-url: /user/logout
  parameter:
    username: username
    password: password
  white-url:
    - /js/**
    - /css/**
    - /images/**
    - /user/login
    - /login.html

二、创建相关的类

UserDetailsService

UserDetailsService接口是Spring Security中非常重要的接口,在登录认证的时候会通过这个接口的loadUserByUsername()方法获取用户的信息,来完成登录的用户名、密码校验,完成登录流程。

我们需要创建一个UserDetailsService的实现类,并声明为Spring组件。

package com.example.security.security;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.security.entity.User;
import com.example.security.exception.GlobalException;
import com.example.security.mapper.UserMapper;
import com.example.security.restful.ResponseCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

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

/**
 * @author heyunlin
 * @version 1.0
 */
@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    private final UserMapper userMapper;

    @Autowired
    public UserDetailsServiceImpl(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 查询用户信息
        User user = selectByUsername(username);

        if (user == null) {
            throw new BadCredentialsException("登录失败,用户名不存在!");
        } else {
            List permissions = selectPermissions(username);

            return org.springframework.security.core.userdetails.User.builder()
                    .username(user.getUsername())
                    .password(user.getPassword())
                    .accountExpired(false)
                    .accountLocked(false)
                    .disabled(!user.getEnable())
                    .credentialsExpired(false)
                    .authorities(permissions.toArray(new String[] {}))
                    .build();
        }
    }

    /**
     * 通过用户名查询用户信息
     * @param username 用户名
     * @return User
     */
    private User selectByUsername(String username) {
        QueryWrapper wrapper = new QueryWrapper<>();

        wrapper.eq("username", username);

        List list = userMapper.selectList(wrapper);

        if (list.size() == 1) {
            return list.get(0);
        }

        return null;
    }

    /**
     * 通过用户名查询用户权限
     * @param username 用户名
     * @return List
     */
    private List selectPermissions(String username) {
        if (username == null) {
            throw new GlobalException(ResponseCode.BAD_REQUEST, "用户名不能为空");
        }

        List permissions = new ArrayList<>();

        permissions.add("/user/login");
        permissions.add("/user/logout");
        permissions.add("/user/selectById");

        return permissions;
    }

}

SecurityConfig.java

创建security的配置类

package com.example.security.config;

import com.example.security.security.LoginFailHandler;
import com.example.security.security.LoginSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author heyunlin
 * @version 1.0
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final SystemProperties systemProperties;

    @Autowired
    public SecurityConfig(SystemProperties systemProperties) {
        this.systemProperties = systemProperties;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new PasswordEncoder() {

            @Override
            public String encode(CharSequence charSequence) {
                return (String) charSequence;
            }

            @Override
            public boolean matches(CharSequence charSequence, String s) {
                return charSequence.equals(s);
            }
        };
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 禁用防跨域攻击
        http.csrf().disable();

        // 配置各请求路径的认证与授权
        http.formLogin()
                .loginPage(systemProperties.getLoginPage()) // 自定义登录页面的地址
                .loginProcessingUrl(systemProperties.getLoginUrl()) // 处理登录的接口地址
                .usernameParameter(systemProperties.getParameter().get("username")) // 用户名的参数名
                .passwordParameter(systemProperties.getParameter().get("password")) // 密码的参数名
                .successHandler(new LoginSuccessHandler(systemProperties))
                //.successForwardUrl("/index.html") // 登录成功跳转的地址
                .failureHandler(new LoginFailHandler()); // 登录失败的处理器

        // 退出登录相关配置
        http.logout()
                .logoutUrl(systemProperties.getLogoutUrl()) // 退出登录的接口地址
                .logoutSuccessUrl(systemProperties.getLoginUrl()); // 退出登录成功跳转的地址

        // 配置认证规则
        String[] toArray = systemProperties.getWhiteUrl().toArray(new String[]{});
        http.authorizeRequests()
                .antMatchers(toArray).permitAll() // 白名单,也就是不需要登录也能访问的资源
                .anyRequest().authenticated();
    }

}

SystemProperties.java

package com.example.security.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

/**
 * @author heyunlin
 * @version 1.0
 */
@Data
@Component
@ConfigurationProperties(prefix = "system")
public class SystemProperties {

    /**
     * 登录页面
     */
    private String loginPage;

    /**
     * 登录的请求地址
     */
    private String loginUrl;

    /**
     * 登录成功后跳转的页面
     */
    private String indexPage;

    /**
     * 退出登录的请求地址
     */
    private String logoutUrl;

    /**
     * 白名单
     */
    private List whiteUrl;

    /**
     * 登录的参数
     */
    private Map parameter;
}

MybatisPlusConfig.java

package com.example.security.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author heyunlin
 * @version 1.0
 */
@Configuration
@MapperScan(basePackages = "com.example.security.mapper")
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        // 防全表更新与删除插件
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        // 分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));

        return interceptor;
    }

}

三、完成登录接口

创建数据库实体类

User.java

package com.example.security.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * 用户
 * @author heyunlin
 * @version 1.0
 */
@Data
@TableName("user")
public class User implements Serializable {
	private static final long serialVersionUID = 18L;

	@TableId(value = "id", type = IdType.INPUT)
	private String id;

	/**
	 * 姓名
	 */
	private String name;

	/**
	 * 性别
	 */
	private Integer gender;

	/**
	 * 用户名
	 */
	private String username;

	/**
	 * 密码
	 */
	private String password;

	/**
	 * 手机号
	 */
	private String phone;

	/**
	 * 是否启用
	 */
	private Boolean enable;

	/**
	 * 最后一次登录时间
	 */
	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
	private LocalDateTime lastLoginTime;
}

创建持久层接口

package com.example.security.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.security.entity.User;
import org.springframework.stereotype.Repository;

/**
 * @author heyunlin
 * @version 1.0
 */
@Repository
public interface UserMapper extends BaseMapper {

}

创建登录DTO对象

package com.example.security.dto;

import lombok.Data;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;

/**
 * @author heyunlin
 * @version 1.0
 */
@Data
public class UserLoginDTO implements Serializable {
    private static final long serialVersionUID = 18L;

    /**
     * 用户名
     */
    @NotNull(message = "用户名不允许为空")
    @NotEmpty(message = "用户名不允许为空")
    private String username;

    /**
     * 密码
     */
    @NotNull(message = "密码不允许为空")
    @NotEmpty(message = "密码不允许为空")
    private String password;
}

创建控制器类

package com.example.security.controller;

import com.example.security.dto.UserLoginDTO;
import com.example.security.entity.User;
import com.example.security.restful.JsonResult;
import com.example.security.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author heyunlin
 * @version 1.0
 */
@RestController
@RequestMapping(path = "/user", produces = "application/json;charset=utf-8")
public class UserController {

    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public JsonResult login(@Validated UserLoginDTO userLoginDTO) {
        userService.login(userLoginDTO);

        return JsonResult.success("登录成功");
    }

    @RequestMapping(value = "/logout", method = RequestMethod.POST)
    public JsonResult logout() {
        userService.logout();

        return JsonResult.success("登出成功");
    }

    @RequestMapping(value = "/selectById", method = RequestMethod.GET)
    public JsonResult selectById(@RequestParam(value = "id", required = true) String userId) {
        User user = userService.selectById(userId);

        return JsonResult.success(null, user);
    }

}

创建业务层类

UserService接口

package com.example.security.service;

import com.example.security.dto.UserLoginDTO;
import com.example.security.entity.User;

/**
 * @author heyunlin
 * @version 1.0
 */
public interface UserService {

    /**
     * 登录认证
     * @param userLoginDTO 用户登录信息
     */
    void login(UserLoginDTO userLoginDTO);

    /**
     * 退出登录
     */
    void logout();

    /**
     * 通过ID查询用户信息
     * @param userId 用户ID
     * @return User 通过ID查询到的用户信息
     */
    User selectById(String userId);
}

UserServiceImpl.java

package com.example.security.service.impl;

import com.example.security.dto.UserLoginDTO;
import com.example.security.entity.User;
import com.example.security.mapper.UserMapper;
import com.example.security.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

/**
 * @author heyunlin
 * @version 1.0
 */
@Service
public class UserServiceImpl implements UserService {

    private final UserMapper userMapper;
    private final AuthenticationManager authenticationManager;

    @Autowired
    public UserServiceImpl(UserMapper userMapper, AuthenticationManager authenticationManager) {
        this.userMapper = userMapper;
        this.authenticationManager = authenticationManager;
    }

    @Override
    public void login(UserLoginDTO userLoginDTO) {
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                userLoginDTO.getUsername(),
                userLoginDTO.getPassword()
        );

        authenticationManager.authenticate(authentication);
    }

    @Override
    public void logout() {
        // todo
    }

    @Override
    public User selectById(String userId) {
        return userMapper.selectById(userId);
    }

}

自定义登录成功处理器

登陆成功直接重定向到/index.html

package com.example.security.security;

import com.example.security.config.SystemProperties;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

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

/**
 * @author heyunlin
 * @version 1.0
 */
public class LoginSuccessHandler implements AuthenticationSuccessHandler {

    private final SystemProperties systemProperties;

    public LoginSuccessHandler(SystemProperties systemProperties) {
        this.systemProperties = systemProperties;
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        response.sendRedirect(systemProperties.getIndexPage());
    }

}

至此,springboot整合Spring Security就完成了,项目结构如下。

Spring Security入门教程,springboot整合Spring Security_第1张图片

文章就分享到这里了,代码已开源,可按需获取~

springboot整合spring securityicon-default.png?t=N7T8https://gitee.com/he-yunlin/springboot-springsecurity.git

你可能感兴趣的:(常用开源框架,中间件的使用,spring,spring,boot,java)