网站下载图标,秦州区建设局网站,网站建设合同不给版权,05网课时作业本答案目录 引言1.Springboot结合SpringSecurity用户认证流程1.1 配置pom文件1.2.配置application.yml 2.自定义MD5加密3.BCryptPasswordEncoder密码编码器4.RememberMe记住我的实现5.CSRF防御5.1.什么是CSRF 引言
上篇网址
1.Springboot结合SpringSecurity用户认证流程
1.1 配置p… 目录 引言1.Springboot结合SpringSecurity用户认证流程1.1 配置pom文件1.2.配置application.yml 2.自定义MD5加密3.BCryptPasswordEncoder密码编码器4.RememberMe记住我的实现5.CSRF防御5.1.什么是CSRF 引言
上篇网址
1.Springboot结合SpringSecurity用户认证流程
1.1 配置pom文件
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-devtools/artifactIdscoperuntime/scopeoptionaltrue/optional/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencydependencygroupIdorg.springframework.security/groupIdartifactIdspring-security-test/artifactIdscopetest/scope/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-freemarker/artifactId/dependencydependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.2/version/dependencydependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-generator/artifactIdversion3.5.2/version/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId/dependency1.2.配置application.yml
server:port: 8080
spring:freemarker:# 设置freemarker模板后缀suffix: .ftl# 设置freemarker模板前缀template-loader-path: classpath:/templates/enabled: truedatasource:driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 123456url: jdbc:mysql://localhost:3306/bookshop导入表sys_user mybatisplus生成
package com.yuan.springsecurity1.config;import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import lombok.extern.slf4j.Slf4j;import java.util.Arrays;
import java.util.Collections;
import java.util.List;Slf4j
public class MySQLGenerator {private final static String URL jdbc:mysql://localhost:3306/bookshop;private final static String USERNAME root;private final static String PASSWORD 123456;private final static DataSourceConfig.Builder DATA_SOURCE_CONFIG new DataSourceConfig.Builder(URL, USERNAME, PASSWORD);public static void main(String[] args) {FastAutoGenerator.create(DATA_SOURCE_CONFIG).globalConfig((scanner, builder) -builder.author(scanner.apply(请输入作者名称?)).outputDir(System.getProperty(user.dir) \\src\\main\\java).commentDate(yyyy-MM-dd).dateType(DateType.TIME_PACK).enableSwagger()).packageConfig((builder) -builder.parent(com.yuan.springsecurity1).entity(pojo).service(service).serviceImpl(service.impl).mapper(mapper).xml(mapper.xml).pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty(user.dir) \\src\\main\\resources\\mapper))).injectionConfig((builder) -builder.beforeOutputFile((a, b) - log.warn(tableInfo: a.getEntityName()))).strategyConfig((scanner, builder) -builder.addInclude(getTables(scanner.apply(请输入表名,多个英文逗号分隔?所有输入 all))).addTablePrefix(tb_, t_, lay_, meeting_, sys_, t_medical_).entityBuilder().enableChainModel().enableLombok().enableTableFieldAnnotation().controllerBuilder().enableRestStyle().enableHyphenStyle().build()).templateEngine(new FreemarkerTemplateEngine()).execute();}protected static ListString getTables(String tables) {return all.equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(,));}}实体类
package com.yuan.springsecurity1.pojo;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;/*** p* 用户信息表* /p** author yuan* since 2023-12-21*/
Getter
Setter
Accessors(chain true)
TableName(sys_user)
public class User implements Serializable, UserDetails {private static final long serialVersionUID 1L;TableId(value id, type IdType.AUTO)private Integer id;TableField(username)private String username;TableField(password)private String password;TableField(real_name)private String realName;TableField(exist false)private ListGrantedAuthority authorities;TableField(account_non_expired)private boolean accountNonExpired;TableField(account_non_locked)private boolean accountNonLocked;TableField(credentials_non_expired)private boolean credentialsNonExpired;TableField(enabled)private boolean enabled;}
实现类
package com.yuan.springsecurity1.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.yuan.springsecurity1.pojo.User;
import com.yuan.springsecurity1.mapper.UserMapper;
import com.yuan.springsecurity1.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
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;/*** p* 用户信息表 服务实现类* /p** author yuan* since 2023-12-21*/
Service
public class UserServiceImpl extends ServiceImplUserMapper, User implements IUserService, UserDetailsService {Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {return getOne(new QueryWrapperUser().eq(username,username));}
}
security配置类
package com.yuan.springsecurity1.config;import com.yuan.springsecurity1.service.impl.UserServiceImpl;
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.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;Configuration
//开启SpringSecurity的默认行为
EnableWebSecurity
public class WebSecurityConfig {Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}Autowiredprivate UserServiceImpl userService;/*** 获取AuthenticationManager认证管理器登录时认证使用基于数据库方式* param* return* throws Exception*/Beanpublic AuthenticationManager authenticationManager() throws Exception {//创建DaoAuthenticationProviderDaoAuthenticationProvider providernew DaoAuthenticationProvider();//设置userDetailsService基于数据库方式进行身份认证provider.setUserDetailsService(userService);//配置密码编码器provider.setPasswordEncoder(passwordEncoder());return new ProviderManager(provider);}Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http)throws Exception{http.authorizeRequests()// 开放接口访问权限不需要登录就可以访问.antMatchers(/toLogin).permitAll()//访问路径有admin的方法时需要有ADMIN的身份.antMatchers(/admin/**).hasRole(ADMIN).antMatchers(/user/**).hasAnyRole(ADMIN,USER)// 其余所有请求全部需要鉴权认证.anyRequest().authenticated().and().formLogin()// 设置登录页面的 URL.loginPage(/toLogin)// 设置登录请求的 URL即表单提交的 URL.loginProcessingUrl(/userLogin)// 设置登录表单中用户名字段的参数名默认为username.usernameParameter(username)// 设置登录表单中密码字段的参数名默认为password.passwordParameter(password)//成功后的处理.successHandler((req,resp,auth)-{resp.sendRedirect(/index);})//登录失败的处理.failureHandler((req,resp,ex)-{req.setAttribute(msg,ex.getMessage());req.getRequestDispatcher(/toLogin).forward(req,resp);}).and().exceptionHandling().accessDeniedPage(/noAccess).and().logout()// 设置安全退出的URL路径.logoutUrl(/logout)// 设置退出成功后跳转的路径.logoutSuccessUrl(/toLogin) ;http.csrf().disable();return http.build();}
}login.ftl
!DOCTYPE html
html langzh
headmeta charsetUTF-8title/title
/head
body
h1用户登录/h1
form action/userLogin methodpostplabel用户input typetext nameusername//label/pplabel密码input typepassword namepassword//label/pinput typesubmit value登录/
/form
p${msg!}/p
/body
/html2.自定义MD5加密
创建自定义MD5加密类并实现PasswordEncoder
public class CustomMd5PasswordEncoder implements PasswordEncoder {Overridepublic String encode(CharSequence rawPassword) {//对密码进行 md5 加密String md5Password DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes());System.out.println(md5Password);return md5Password;}Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {// 通过md5校验System.out.println(rawPassword);System.out.println(encodedPassword);return encode(rawPassword).equals(encodedPassword);}
}修改SecurityConfig配置类更换密码编码器
Bean
public PasswordEncoder passwordEncoder(){// 自定义MD5加密方式return new CustomMd5PasswordEncoder();
}数据库中的用户密码也需要更换成对应自定义MD5加密密码
//MD5自定义加密方式
String pwd DigestUtils.md5DigestAsHex(123456.getBytes());
System.out.println(pwd);最后将生成的MD5加密密码保存到数据库表中。
3.BCryptPasswordEncoder密码编码器
BCryptPasswordEncoder是Spring Security中一种基于bcrypt算法的密码加密方式。bcrypt算法是一种密码哈希函数具有防止彩虹表攻击的优点因此安全性较高。
使用BCryptPasswordEncoder进行密码加密时可以指定一个随机生成的salt值将其与原始密码一起进行哈希计算。salt值可以增加密码的安全性因为即使两个用户使用相同的密码由于使用不同的salt值进行哈希计算得到的哈希值也是不同的。
在Spring Security中可以通过在SecurityConfig配置类中添加以下代码来使用BCryptPasswordEncoder进行密码加密
Bean
public PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();
}这样就可以在Spring Security中使用BCryptPasswordEncoder进行密码加密了。
4.RememberMe记住我的实现
在实际开发中为了用户登录方便常常会启用记住我Remember-Me功能。如果用户登录时勾选了“记住我”选项那么在一段有效时间内会默认自动登录免去再次输入用户名、密码等登录操作。该功能的实现机理是根据用户登录信息生成 Token 并保存在用户浏览器的 Cookie 中当用户需要再次登录时自动实现校验并建立登录态的一种机制。
Spring Security提供了两种 Remember-Me 的实现方式
简单加密 Token用散列算法加密用户必要的登录系信息并生成 Token 令牌。持久化 Token数据库等持久性数据存储机制用的持久化 Token 令牌。
基于持久化Token配置步骤如下
创建数据库表 persistent_logins用于存储自动登录信息
CREATE TABLE persistent_logins (username varchar(64) NOT NULL,series varchar(64) PRIMARY KEY,token varchar(64) NOT NULL,last_used timestamp NOT NULL
);该步骤可以不做在后续的配置过程中可以交由SpringSecurity自动生成。 基于持久化Token配置修改SecurityConfig配置类
package com.yuan.springsecurity1.config;import com.yuan.springsecurity1.service.impl.UserServiceImpl;
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.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;import javax.sql.DataSource;Configuration
//开启SpringSecurity的默认行为
EnableWebSecurity
public class WebSecurityConfig {Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}Autowiredprivate DataSource dataSource;/*** 配置持久化Token方式注意tokenRepository.setCreateTableOnStartup()配置*/Beanpublic PersistentTokenRepository persistentTokenRepository(){JdbcTokenRepositoryImpl tokenRepository new JdbcTokenRepositoryImpl();tokenRepository.setDataSource(dataSource);// 设置为true要保障数据库该表不存在不然会报异常哦// 所以第二次打开服务器应用程序的时候得把它设为falsetokenRepository.setCreateTableOnStartup(false);return tokenRepository;}Autowiredprivate UserServiceImpl userService;/*** 获取AuthenticationManager认证管理器登录时认证使用基于数据库方式* param* return* throws Exception*/Beanpublic AuthenticationManager authenticationManager() throws Exception {//创建DaoAuthenticationProviderDaoAuthenticationProvider providernew DaoAuthenticationProvider();//设置userDetailsService基于数据库方式进行身份认证provider.setUserDetailsService(userService);//配置密码编码器provider.setPasswordEncoder(passwordEncoder());return new ProviderManager(provider);}Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http)throws Exception{http.authorizeRequests()// 开放接口访问权限不需要登录就可以访问.antMatchers(/toLogin).permitAll()//访问路径有admin的方法时需要有ADMIN的身份.antMatchers(/admin/**).hasRole(ADMIN).antMatchers(/user/**).hasAnyRole(ADMIN,USER)// 其余所有请求全部需要鉴权认证.anyRequest().authenticated().and().formLogin()// 设置登录页面的 URL.loginPage(/toLogin)// 设置登录请求的 URL即表单提交的 URL.loginProcessingUrl(/userLogin)// 设置登录表单中用户名字段的参数名默认为username.usernameParameter(username)// 设置登录表单中密码字段的参数名默认为password.passwordParameter(password)//成功后的处理.successHandler((req,resp,auth)-{resp.sendRedirect(/index);})//登录失败的处理.failureHandler((req,resp,ex)-{req.setAttribute(msg,ex.getMessage());req.getRequestDispatcher(/toLogin).forward(req,resp);}).and().exceptionHandling().accessDeniedPage(/noAccess).and().logout()// 设置安全退出的URL路径.logoutUrl(/logout)// 设置退出成功后跳转的路径.logoutSuccessUrl(/toLogin).and().rememberMe()// 指定 rememberMe 的参数名用于在表单中携带 rememberMe 的值。.rememberMeParameter(remember-me)// 指定 rememberMe 的有效期单位为秒默认2周。.tokenValiditySeconds(600)// 指定 rememberMe 的 cookie 名称。.rememberMeCookieName(remember-me-cookie)// 指定 rememberMe 的 token 存储方式可以使用默认的 PersistentTokenRepository 或自定义的实现。.tokenRepository(persistentTokenRepository())// 指定 rememberMe 的认证方式需要实现 UserDetailsService 接口并在其中查询用户信息。.userDetailsService(userService);http.csrf().disable();return http.build();}
}登录界面添加 input typecheckbox nameremember-me/记住我br/5.CSRF防御
5.1.什么是CSRF
CSRFCross-Site Request Forgery跨站请求伪造是一种利用用户已登录的身份在用户不知情的情况下发送恶意请求的攻击方式。攻击者可以通过构造恶意链接或者伪造表单提交等方式让用户在不知情的情况下执行某些操作例如修改密码、转账、发表评论等。
为了防范CSRF攻击常见的做法是在请求中添加一个CSRF Token也叫做同步令牌、防伪标志并在服务器端进行验证。CSRF Token是一个随机生成的字符串每次请求都会随着请求一起发送到服务器端服务器端会对这个Token进行验证如果Token不正确则拒绝执行请求。
修改SecurityConfig配置类把这个删掉
http.csrf().disable();login.ftl中添加
input typehidden name${_csrf.parameterName} value${_csrf.token}/总的来说不带 name“${_csrf.parameterName}” 这个的话会就像是没带身份证一样不能提交表单反之者可通过验证提交表单