简介
Spring Security是一个能够为基于Spring的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能.
为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。(为什么要学)
第一个被称为“认证”,是为用户建立一个他所声明的主体。主体一般是指用户,设备或可以在系统中执行动作的其他系统。
第二个叫“授权”,指的是一个用户能否在应用中执行某个操作,在到达授权判断之前,身份的主体已经由身份验证过程建立。
这些概念是通用的,不是Spring Security特有的。
上代码:
数据库:
pom:
<?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.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springBootStudy</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springBootStudy</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<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>
<scope>test</scope>
</dependency>
<!-- mybatis-plus依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- security架包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
//thymeleaf依赖
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
</dependencies>
</project>
pojo:
package com.zhang.pojo;
import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.time.LocalDateTime;
import java.util.Date;
/**
* @author 小乌龟
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
/**
* 数据库主键生成 uuid id自增 雪花算法(mybatis-plus默认选择)
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String username;
private Integer age;
private String email;
/**
* 注意!这里需要标记为填充字段
*/
@TableField( fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField( fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 开启乐观锁
*/
@Version
private Integer version;
private Integer deleted;
private String password;
private String perms;
}
application.yml
#数据库连接配置
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/mybatis-plus?useSSL=false&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
thymeleaf:
cache: false
#日志配置 配置逻辑删除
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置 @TableLogic private Integer deleted;)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
securityConfig
package com.zhang.config;
import com.zhang.service.Impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @author 小乌龟
*/
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserServiceImpl userService;
//请求授权验证
@Override
protected void configure(HttpSecurity http) throws Exception {
//有角色权限的一定要放在比较上面,要不会被其他的覆盖了 测试所得
// .denyAll(); //拒绝访问
// .authenticated(); //需认证通过
// .permitAll(); //无条件允许访问
// 访问权限
http.authorizeRequests()
.antMatchers("/write").hasRole("writer")
.antMatchers("/","/index").permitAll()
.antMatchers("/register","/login","/toLogin").permitAll()
.antMatchers("/*").authenticated();
// 登录配置
// 登陆表单提交请求
// 设置默认登录成功后跳转的页面
http.formLogin()
.usernameParameter("username")
.passwordParameter("password")
.loginPage("/toLogin")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/success");
// 注销配置
http.headers().contentTypeOptions().disable();
http.headers().frameOptions().disable(); // 图片跨域
http.csrf().disable();//关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求
http.logout().logoutSuccessUrl("/");
// 记住我配置
http.rememberMe().rememberMeParameter("remember");
}
/**
* 用户授权验证
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
// 密码加密方式
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
注意:
MybatisPlusConfig
package com.zhang.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
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;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* @author 小乌龟
*/
@MapperScan("com.zhang.mapper")
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//乐观锁配置
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
//最新版分页配置
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
}
userMapper
package com.zhang.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zhang.pojo.User;
import org.springframework.stereotype.Repository;
/**
* @author 小乌龟
* 代表持久层 所有的CRUD语句已经写好了 在BaseMapper配置文件中
*/
@Repository
public interface UserMapper extends BaseMapper<User> {
}
userService
package com.zhang.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zhang.pojo.User;
/**
* @author 小乌龟
*/
public interface UserService extends IService<User> {
}
userServiceImpl
package com.zhang.service.Impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zhang.pojo.User;
import com.zhang.mapper.UserMapper;
import com.zhang.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhang.utils.BaseUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
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.stereotype.Service;
import javax.management.relation.Role;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author 小乌龟
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService,UserDetailsService {
@Autowired
UserService userService;
@Autowired
HttpSession session;
/**
* 用户登录逻辑和验证处理
* @param s
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
// 通过用户名查询用户
User user = userService.getOne(new QueryWrapper<User>().eq("username", s));
// 放入session
session.setAttribute("loginUser",user);
//创建一个新的UserDetails对象,最后验证登陆的需要
UserDetails userDetails = null;
if(user!=null){
/**System.out.println("未加密:"+user.getPassword());
* String BCryptPassword = new BCryptPasswordEncoder().encode(user.getPassword());
* 登录后会将登录密码进行加密,然后比对数据库中的密码,数据库密码需要加密存储!
*/
String password = user.getPassword();
//创建一个集合来存放权限
Collection<GrantedAuthority> authorities = getAuthorities(user);
//实例化UserDetails对象
userDetails=new org.springframework.security.core.userdetails.User(s,password,
true,
true,
true,
true, authorities);
}
return userDetails;
}
/**
* 获取角色信息
* @param user
* @return
*/
private Collection<GrantedAuthority> getAuthorities(User user){
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
User role = userService.getById(user.getId());
//注意:这里每个权限前面都要加ROLE_。否在最后验证不会通过
authList.add(new SimpleGrantedAuthority("ROLE_"+role.getPerms()));
return authList;
}
}
MyMetaObjectHandler
package com.zhang.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* @author 小乌龟
*/
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
// 起始版本 3.3.0(推荐使用)
// default <T> MetaObjectHandler strictInsertFill(MetaObject metaObject, String fieldName, Class<T> fieldType, Object fieldVal)
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
// 起始版本 3.3.0(推荐)
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
RouteController
package com.zhang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author 小乌龟
*/
@Controller
public class RouteController {
@RequestMapping("/toLogin")
public String toLogin(){
return "views/login";
}
@RequestMapping("/success")
public String loginSecures(){
return "views/success";
}
@RequestMapping("/write")
public String toWrite(){
return "views/write";
}
}
静态资源 模板
css
@charset "utf-8";
/* CSS Document */
* {
margin: 0;
padding: 0;
}
li {
list-style: none;
}
a {
text-decoration: none;
color:#ef9609;
}
body {
width: 1200px;
margin: 0 auto;
background-color: #f6f6f6;
}
/*头部start*/
.top {
position: relative;
height: 53px;
color: #ef9609;
font-size: 20px;
}
.top .logo {
position: absolute;
top: 0;
left: 87px;
z-index: -1;
}
.top span {
color: #CCC;
font-size: 12px;
}
/*头部end*/
/*中心模板start*/
.center {
height: 600px;
background:url(../img/login.jpg) no-repeat;
}
.center .loginUser {
float: right;
width: 542px;
height: 544px;
background-color: #FFF;
}
.center .loginUser ul h2 {
height: 30px;
text-align: center;
margin: 20px auto;
font-family: "Microsoft YaHei UI Light";
color: #ff6700;
}
.center .loginUser ul li {
width: 299px;
height: 17px;
color: #FFF;
border: 1px solid #000;
padding: 20px;
margin: 18px 40px 19px 108px;
}
.center .loginUser ul li .login {
border: none;
font-size: 17px;
outline: none;
}
.center .loginUser .button {
display: inline-block;
margin-left: 135px;
width: 299px;
height: 45px;
background-color: lightpink;
border: antiquewhite;
line-height: 45px;
text-align: center;
font-size: 20px;
color: #fff;
}
/*中心模板end*/
/*尾部start*/
.bottom .trademark {
width: 1200px;
height: 30px;
text-align: center;
line-height: 30px;
font-size: 12px;
color: #CCC;
}
.bottom .trademark a {
color: #ff6700;
}
/*尾部end*/
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>登入页面</title>
<link rel="stylesheet" type="text/css" href="../css/Login.css">
</head>
<div>
<!-- 头部 -->
<div class="top">
<h2>图书管理员</h2>
<span>用心管理你的每一本图书</span></div>
<!-- 中心 -->
<div class="center">
<div class="loginUser">
<form action="/login" method="post">
<ul>
<h2>账号登入</h2>
<li>
<input type="text" name="username" placeholder="用户名" class="login">
</li>
<li>
<input type="password" name="password" placeholder="密码" class="login">
</li>
</ul>
<input class="button" type="submit" value="登入">
</form>
</div>
</div>
<!-- 尾部 -->
<div class="bottom">
<hr>
<div class="trademark">©版权所有20软工一班zjr所有<br>
<a href="https://www.mi.com/">小米商城</a><br>
非常感谢 </div>
</div>
</div>
</body>
</html>
write.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>编辑页面</h1>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>hello word</h1>
</body>
</html>
sucess.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登入成功</h1>
</body>
</html>