编写:Horin
文章目录
前言
Spring 安全性是提供认证、授权和防范常见攻击的框架。由于对imperative和reactive应用程序的安全都提供了第一类支持,因此它是保护基于 Spring 的应用程序的事实上的标准。
有关功能的完整列表,请参见引用的Features部分。
Spring安全 中文文档
提示:以下是本篇文章正文内容,下面案例可供参考
一、导入框架依赖
1、添加依赖:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>TheToken</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>TheToken</name>
<description>TheToken</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>
<!-- Spring boot 热部署 导入一个依赖即可-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- thymeleaf模板代码 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 安全管理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Thymeleaf 整合 Spring Security 组件-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.6.7</version>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
二、操作流程
1.继承WebSecurityConfigurerAdapter并重写方法
配置类:SecurityConfig.java
package com.example.thetoken.config;
import com.example.thetoken.service.serviceImpl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.AccessDeniedException;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
@EnableWebSecurity // 开启MVC security安全支持
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
/**
* userDetailsService身份验证
*/
@Autowired
private UserDetailsServiceImpl userDetailsService;
/**
* 用户授权管理自定义配置
* @param http
* @throws Exception
*/
/**
* 用户身份认证自定义配置
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/**
* 明文加密
*/
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
/**
* 1、内存认证
*/
// auth.inMemoryAuthentication().passwordEncoder(encoder)
// .withUser("jsor").password(encoder.encode("12312333")).roles("common")
// .and()
// .withUser("wanglaoji").password(encoder.encode("123456")).roles("admin");
/**
* 2、UserDetailsService认证
*/
auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
}
}
2.实现UserDetailsService接口
实现类:UserDetailsServiceImpl.java
package com.example.thetoken.service.serviceImpl;
import com.example.thetoken.domain.User;
import com.example.thetoken.domain.Authority;
import com.example.thetoken.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Classname UserDetailsServiceImpl
* @Description 自定义一个UserDetailsService接口实现类进行用户认证信息封装
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
/**
* 查询用户信息
* selectUser():通过用户名查询用户密码
* selectAuths():查询该用户权限
*/
User user = userMapper.selectUser(s);
List<Authority> authorities = userMapper.selectAuths(s);
/**
* 封装用户权限
*/
List<SimpleGrantedAuthority> list = authorities.stream().map(
authority -> new SimpleGrantedAuthority(authority.getAuthority())).collect(Collectors.toList());
/**
* 将对应值传入security的User对象
*/
if(user!=null){
System.out.println(user.getUserName()+" "+user.getUserAccount()+" "+user.getUserPassword());
String userA = user.getUserAccount();
String pasA = user.getUserPassword();
System.out.println(list);
//数据库为明文密码,所以使用对象来转换为需要的加密格式
BCryptPasswordEncoder bCryptPasswordEncoder=new BCryptPasswordEncoder();
UserDetails userDetails=
new org.springframework.security.core.userdetails.User(userA, bCryptPasswordEncoder.encode(pasA), list);
System.out.println(userDetails);
return userDetails;
} else {
/**
* 不存在用户抛出为空
*/
return null;
}
}
}
3.编写数据持久化接口
实现类:UserMapper.java
package com.example.thetoken.mapper;
import com.example.thetoken.domain.Authority;
import com.example.thetoken.domain.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
public interface UserMapper {
//验证用户账号是否匹配
User selectUser(String userAccount);
//获取该用户名的权限
List<Authority> selectAuths(String userAccount);
}
4.创建数据库
SQL导出文件:springbootf.sql
/*
Navicat MySQL Data Transfer
Source Server : 127.0.0.1
Source Server Type : MySQL
Source Server Version : 50723
Source Host : localhost:3306
Source Schema : springbootf
Target Server Type : MySQL
Target Server Version : 50723
File Encoding : 65001
Date: 01/06/2022 19:19:38
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for authority
-- ----------------------------
DROP TABLE IF EXISTS `authority`;
CREATE TABLE `authority` (
`id` int(50) NOT NULL,
`authority` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of authority
-- ----------------------------
INSERT INTO `authority` VALUES (1, 'ROLE_admin');
INSERT INTO `authority` VALUES (2, 'ROLE_common');
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`user_id` int(5) NOT NULL AUTO_INCREMENT,
`user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`user_account` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`user_password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`sex` int(1) NOT NULL COMMENT '0-女,1-男',
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, '江治雨', '8823', 'j12312333', 1);
INSERT INTO `user` VALUES (2, '周杰伦', '123', '123', 1);
INSERT INTO `user` VALUES (4, '邓丽君', 'dengli312884', '987654321', 0);
INSERT INTO `user` VALUES (6, '张国荣', '401', 'zgr', 1);
INSERT INTO `user` VALUES (7, 's', 's', '', 1);
-- ----------------------------
-- Table structure for user_authority
-- ----------------------------
DROP TABLE IF EXISTS `user_authority`;
CREATE TABLE `user_authority` (
`id` int(20) NOT NULL,
`user_id` int(50) NOT NULL,
`authority_id` int(50) NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user_authority
-- ----------------------------
INSERT INTO `user_authority` VALUES (1, 1, 1);
INSERT INTO `user_authority` VALUES (2, 2, 2);
SET FOREIGN_KEY_CHECKS = 1;
5.Mapper映射文件
映射文件:UserMapper.xml
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.thetoken.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.example.thetoken.domain.User">
<id property="userId" column="user_id"/>
<result property="userName" column="user_name"/>
<result property="userAccount" column="user_account"/>
<result property="userPassword" column="user_password"/>
<result property="sex" column="sex"/>
</resultMap>
<select id="selectUser" parameterType="string" resultMap="BaseResultMap">
select * from user where user_account = #{userAccount}
</select>
<select id="selectAuths" parameterType="string" resultType="Authority">
select a.* from user c, authority a, user_authority ca
where
ca.user_id=c.user_id and ca.authority_id=a.id and c.user_account =#{userAccount}
</select>
</mapper>
6.编写实体类
省略:User实体类、Authority实体类
7.完善SecurityConfig配置:授权、自定义页面等
配置类:SecurityConfig.java
package com.example.thetoken.config;
import com.example.thetoken.service.serviceImpl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.AccessDeniedException;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
@EnableWebSecurity // 开启MVC security安全支持
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
/**
* 第三种身份验证
*/
@Autowired
private UserDetailsServiceImpl userDetailsService;
/**
* 用户授权管理自定义配置
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// super.configure(http);
/**
* 自定义授权
*/
http.authorizeRequests()
// 静态资源、登录、注册页面放行
.antMatchers("/login/**","/moban/**","/toLogin","/toZhuce").permitAll()
// .antMatchers("/templates/**").hasRole("common")
//管理员可以访问用户列表(接口list)
.antMatchers("/list").hasRole("admin")
.anyRequest().authenticated();
/**
* 自定义登录
*/
http.formLogin()
//跳转登陆页面接口
.loginPage("/toLogin")
//以下Controller接口无需编写
.loginProcessingUrl("/loginss")
//主页面接口
.defaultSuccessUrl("/");
/**
* 记住我
*/
http.rememberMe()
.rememberMeParameter("rememberme")
.tokenValiditySeconds(2000);
// 对cookie信息进行持久化管理
// .tokenRepository(tokenRepository());
/**
* 自定义无权限跳转页面
*/
http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
// 如果是权限访问异常,则进行拦截到指定错误页面
RequestDispatcher dispatcher = httpServletRequest.getRequestDispatcher("/err");
dispatcher.forward(httpServletRequest, httpServletResponse);
}
});
}
/**
* 持久化Token存储
* @return
*/
@Bean
public JdbcTokenRepositoryImpl tokenRepository(){
JdbcTokenRepositoryImpl jr=new JdbcTokenRepositoryImpl();
jr.setDataSource(dataSource);
jr.setCreateTableOnStartup(false);
return jr;
}
/**
* 用户身份认证自定义配置
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/**
* 明文加密
*/
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
/**
* 1、内存认证
*/
// auth.inMemoryAuthentication().passwordEncoder(encoder)
// .withUser("jsor").password(encoder.encode("12312333")).roles("common")
// .and()
// .withUser("wanglaoji").password(encoder.encode("123456")).roles("admin");
/**
* 2、UserDetailsService认证
*/
auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
}
}
8.登录页面修改为form表单提交
登录页面:index.html
注意事项:仅供参考结构,未上传css样式文件。
重点一,input的name属性必须固定,账号为username,密码为password。
重点二,form标签的action与配置类中的登录接口一样,spring会自动创建。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>欢迎登录</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!-- style CSS -->
<link th:href="@{/login/css/style.css}" rel="stylesheet">
<!-- google fonts -->
<link th:href="@{https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700&display=swap}" rel="stylesheet">
<script th:src="@{/login/js/jquery-3.1.1.min.js}"></script>
<script th:src="@{/login/js/jquery.min.js}"></script>
</head>
<body>
<script th:if="${userList!=null}"> alert("账号或者密码输入错误!");</script>
<div class="main">
<div class="logo text-center">
<h1> <a href="index.html">叭叭贰叁™ 进销系统</a></h1>
</div>
<div class="content-w3ls text-center" >
<form th:action="@{/loginss}" th:method="post">
<!-- 用户名-->
<div class="wthree-field">
<input name="username" type="text" placeholder="用户名" />
</div>
<!-- 密码-->
<div class="wthree-field">
<input name="password" type="password" placeholder="密码" />
</div>
<div class="wthree-field">
<button class="btn" type="submit" >登录</button>
</div>
<div class="login-bottom">
<a th:href="@{/toZhuce}" class="">没有账户?创建一个。</a>
</div>
</form>
</div>
<div class="copyright">
<p>© 2021 Universe Signin Form. Made with music | Designed by <a
href="" target="_blank">Horin</a></p>
</div>
</div>
</body>
</html>
总结
其中HTML页面、Controller跳转补充完成,就可以正常运行了。