Spring Security是Spring官方推荐的认证、授权框架,功能相比Apache Shiro功能更丰富也更强大,但是使用起来更麻烦。
如果使用过Apache Shiro,学习Spring Security会比较简单一点,两种框架有很多相似的地方。
目录
首先,通过IntelliJ IDEA创建一个springboot项目,项目名为springboot-springsecurity,在pom.xml中添加相关依赖。
- "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.0modelVersion>
-
- <parent>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-parentartifactId>
- <version>2.3.4.RELEASEversion>
- <relativePath />
- parent>
-
- <groupId>com.examplegroupId>
- <artifactId>springboot-springsecurityartifactId>
- <version>0.0.1-SNAPSHOTversion>
-
- <properties>
- <java.version>1.8java.version>
- <jjwt.version>0.9.1jjwt.version>
- <mysql.version>8.0.28mysql.version>
- <druid.version>1.1.21druid.version>
- <lombok.version>1.18.22lombok.version>
- <mybatis.version>2.2.2mybatis.version>
- <mybatis-plus.version>3.5.1mybatis-plus.version>
- properties>
-
- <dependencies>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-webartifactId>
- dependency>
-
-
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-validationartifactId>
- dependency>
-
-
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-securityartifactId>
- dependency>
-
-
- <dependency>
- <groupId>mysqlgroupId>
- <artifactId>mysql-connector-javaartifactId>
- <version>${mysql.version}version>
- dependency>
-
-
- <dependency>
- <groupId>com.alibabagroupId>
- <artifactId>druidartifactId>
- <version>${druid.version}version>
- dependency>
-
-
- <dependency>
- <groupId>org.projectlombokgroupId>
- <artifactId>lombokartifactId>
- <version>${lombok.version}version>
- dependency>
-
-
- <dependency>
- <groupId>org.mybatis.spring.bootgroupId>
- <artifactId>mybatis-spring-boot-starterartifactId>
- <version>${mybatis.version}version>
- dependency>
-
-
- <dependency>
- <groupId>com.baomidougroupId>
- <artifactId>mybatis-plus-boot-starterartifactId>
- <version>${mybatis-plus.version}version>
- dependency>
-
-
- <dependency>
- <groupId>io.jsonwebtokengroupId>
- <artifactId>jjwtartifactId>
- <version>${jjwt.version}version>
- dependency>
- dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-maven-pluginartifactId>
- plugin>
- plugins>
- build>
- project>
- 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接口是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;
- }
-
- }
创建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();
- }
-
- }
- 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; - }
- 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
{ -
- }
- 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就完成了,项目结构如下。

文章就分享到这里了,代码已开源,可按需获取~
springboot整合spring security
https://gitee.com/he-yunlin/springboot-springsecurity.git