写在前面
在本人刚工作时,如果需要写用户登录模块,我都是使用 session 来完成的。到后面,学习了JWT,然后每次请求携带token字符串,后端解析token字符串,获取用户的一些登录信息。现在,开始学习spring security!
下面的内容,都是根据黑马程序员网站的免费视频进行学习,衷心感谢黑马程序员,哈哈,不多说自己去看视频 2天快速入门Spring Security OAuth2.0认证授权
使用SpringBoot 集成 Spring Security
Flyway 简单了解可以看之前写的笔记:Spring Boot 集成 Flyway
下面的所有代码保存至GitHub: spring-boot-security
示例
这里验证了登录失败,跳转登录页,登录成功,跳转到成功页面,然后访问/r/r1 有权限正常访问,/r/r2 没权限,报403错误
和2 同理
一些基本配置
下面只列出了一部分代码,剩下的需要自己去GitHub上下载。
pom.xml
该项目是使用Maven构建的项目
spring-boot-starter-parent : spring boot 框架
spring-boot-starter-web:spring mvc 框架
spring-boot-starter-security: spring security 权限框架
spring-boot-starter-jdbc: 使用jdbcTemplate,写sql语句
mysql-connector-java:mysql 连接驱动
flyway-core :数据库迁移
lombok:减少代码量
<?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.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cfl</groupId>
<artifactId>spring-security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-security</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--Spring Security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--MySQL-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!--Flyway-->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- 数据库版本管理插件-->
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>7.2.1</version>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: 8001
servlet:
session:
timeout: 30m
cookie:
http-only: true #如果为true,那么浏览器脚本将无法访问cookie
secure: false # 如果为true,则cookie将仅通过HTTPS连接发送
spring:
application:
name: cfl-spring-security
datasource:
url: jdbc:mysql://127.0.0.1:3306/spring-security?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
minPoolSize: 3
maxPoolSize: 10
maxLifetime: 20000
borrowConnectionTimeout: 30
loginTimeout: 30
mainteanceInterval: 60
maxIdleTime: 60
## flyway数据迁移
flyway:
#是否开启
enabled: true
locations: classpath:db/migration #迁移脚本的位置,默认db/migration
baseline-on-migrate: true
table: flyway_schema_history # 记录历史记录的表名称
out-of-order: false
代码
WebSecurityConfig.java
spring security 的配置类
package com.cfl.springsecurity.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
/**
* 类描述:
* spring security 的配置
* EnableGlobalMethodSecurity注解 开启方法资源拦截
* @ClassName WebSecurityConfig
* @Author msi
* @Date 2020/12/29 22:40
* @Version 1.0
*/
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 定义用户信息服务,这里使用的内存方式保存用户信息。
* @return
*/
// @Bean
// public UserDetailsService userDetailsService () {
// InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
// manager.createUser(User.withUsername("zhangsan").password("123456").authorities("p1").build());
// manager.createUser(User.withUsername("lisi").password("123456").authorities("p2").build());
// return manager;
// }
/**
* 定义密码编码器
* @return
*/
/* @Bean
public PasswordEncoder passwordEncoder () {
return NoOpPasswordEncoder.getInstance();
}*/
/**
* BCrypt
* @return
*/
@Bean
public PasswordEncoder passwordEncoder () {
return new BCryptPasswordEncoder();
}
/**
* 配置安全拦截机制(最重要)
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// http.authorizeRequests() 方法有多个子节点,每个macher按照他们的声明顺序执行
.authorizeRequests()
// 指定"/r/r1"URL,拥有p1权限能够访问
.antMatchers("/r/r1").hasAuthority("p1")
// 指定"/r/r2"URL,拥有p2权限能够访问
.antMatchers("/r/r2").hasAuthority("p2")
// 指定了"/r/r3"URL,同时拥有p1和p2权限才能够访问
.antMatchers("/r/r3").access("hasAuthority('p1') and hasAuthority('p2')")
// 指定了除了r1、r2、r3之外"/r/**"资源,同时通过身份认证就能够访问,这里使用SpEL(Spring Expression Language)表达式
.antMatchers("/r/**/").authenticated()
// 剩余的尚未匹配的资源,不做保护
.anyRequest().permitAll()
.and()
// 允许表单登录
.formLogin()
// 登录页面,认证失败自动跳到该地址
.loginPage("/login-view")
// 自定义登录提交地址
.loginProcessingUrl("/login")
// 自定义登录成功的页面地址(认证成功后跳转该地址)
.successForwardUrl("/login-success")
// 我们必须允许所有用户访问我们的登录页(例如为验证的用户),这个 formLogin().permitAll() 方法允许任意用户访问基于表单登录的所有的URL。
.permitAll()
.and()
// 提供系统退出支持
.logout()
// 设置触发退出操作的URL (默认是 /logout )
.logoutUrl("/logout")
// 退出之后跳转的URL。默认是 /login?logout
.logoutSuccessUrl("/login-view")
// 定制的 LogoutSuccessHandler ,用于实现用户退出成功时的处理。如果指定了这个选项那么logoutSuccessUrl() 的设置会被忽略。
// .logoutSuccessHandler(logoutSuccessHandler)
// 添加一个 LogoutHandler ,用于实现用户退出时的清理工作.默认 SecurityContextLogoutHandler 会被添加为最后一个 LogoutHandler 。
// .addLogoutHandler(logoutHandler)
// GET请求
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
// 删除cookie
.deleteCookies("JSESSIONID")
// 指定是否在退出时让 HttpSession 无效。 默认设置为 true
.invalidateHttpSession(true)
.and()
.sessionManagement()
.invalidSessionUrl("/login-view?error=INVALID_SESSION")
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
}
}
MvcConfig.java
配置spring mvc
package com.cfl.springsecurity.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 配置 “/” 地址,重定向到 “/login-view”
registry.addViewController("/").setViewName("redirect:/login-view");
}
}
SpringDataUserDetailService.java
package com.cfl.springsecurity.service;
import com.cfl.springsecurity.dao.UserDao;
import com.cfl.springsecurity.model.UserDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
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.Service;
import java.util.List;
/**
* 类描述:
*
* @ClassName SpringDataUserDetailService
* @Description TODO
* @Author msi
* @Date 2021/1/2 15:20
* @Version 1.0
*/
@Service
public class SpringDataUserDetailService implements UserDetailsService {
@Autowired
private UserDao userDao;
/**
* 根据账号查询用户信息
* @param username 账号
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 使用数据库查询用户信息
UserDto userDto = userDao.getUserByUsername(username);
if (userDto == null) {
// 如果用户查不到,返回null,由 provider 抛异常
return null;
}
// 根据用户的id查询用户的权限
List<String> permissionsByUserId = userDao.findPermissionsByUserId(userDto.getId());
// 将 permissionsByUserId 转成数组
String[] permissionArray = new String[permissionsByUserId.size()];
permissionsByUserId.toArray(permissionArray);
System.out.println("username = " + username);
UserDetails userDetails = User.withUsername(userDto.getUsername())
.password(userDto.getPassword())
.authorities(permissionArray)
.build();
return userDetails;
}
}
最后
代码文件太多,直接去Github下载
spring-boot-security
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/78267.html