【2022-3-18更新】
- 文档结构目录;
- 更新笔记。
<!-- spring security 包含下面两个注释掉的包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.security</groupId>-->
<!-- <artifactId>spring-security-web</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.security</groupId>-->
<!-- <artifactId>spring-security-config</artifactId>-->
<!-- </dependency>-->
Authentication - 认证
Authorization - 授权
package com.example.demo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MyController {
@RequestMapping("/toMain")
public String toLogin(){
return "redirect:main.html";
}
@RequestMapping("/toError")
public String Error(){
return "redirect:error.html";
}
}
package com.example.demo.config;
import com.example.demo.hanlder.MyAccessDeniedHandler;
import com.example.demo.hanlder.MyAuthenticationFailureHandler;
import com.example.demo.hanlder.MyAuthenticationSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
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;
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private MyAccessDeniedHandler myAccessDeniedHandler;
// 下面自定义登录逻辑时需要用到
@Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
// 授权
@Override
protected void configure(HttpSecurity http) throws Exception {
// 请求授权的规则
http.authorizeRequests()
.antMatchers("/login.html").permitAll() // 任何权限都可以访问
.antMatchers("/error.html").permitAll()
// 权限判断 UserDetailsServiceImpl
.antMatchers("/main.html").hasAnyAuthority("admina", "user")
// 还可以通过ip地址来开放权限
// .antMatchers("/main.html").hasIpAddress("****")
// 角色判断 UserDetailsServiceImpl 或者 下面的认证里面设置
// .antMatchers("/main.html").hasRole("abc")
// 使用正则表达式放行访问文件
// .regexMatchers()
// 放行使用 get 方式访问的资源。
// .regexMatchers(HttpMethod.GET,"get").permitAll()
// 主要适用于 servletPath 路径请求的方式
// servletPath - 在 .properties 中进行配置的项目路径。 -- 一般使用 antMatchers 已经足够了
// .mvcMatchers("/demo").servletPath("/xxx").permitAll()
.anyRequest().authenticated(); // 所有请求必须被认证 (必须登录之后被访问)
/**
* <p>
* 没有权限默认事件 - 比如登录界面
* </p>
*
* loginPage - 自定义登录界面
* loginProcessingUrl - 当发现此参数时认为是登录;就是实际登录访问的接口地址,必须和表单提交的地址一样 然后执行 UserDetailsServiceImpl
* usernameParameter - 设置表单传参的属性
* passwordParameter - 设置表单传参的属性
* successForwardUrl - 登录成功跳转页面。 post 请求。-- 需要写 MyController
* failureForwardUrl - 登录失败跳转界面。post 请求。-- 需要写 MyController
* successHandler - 登录成功后处理器,不能和 successForwardUrl 共存
* failureHandler - 登录失败后处理器,不能和 failureForwardUrl 共存
*/
http.formLogin()
.loginPage("/login.html")
.usernameParameter("user")
.passwordParameter("pwd")
.loginProcessingUrl("/loogin")
// .successForwardUrl("/toMain")
// .failureForwardUrl("/toError");
.successHandler(new MyAuthenticationSuccessHandler("/main.html"))
.failureHandler(new MyAuthenticationFailureHandler("/error.html"));
// 防跨站攻击 关闭csrf功能 - 登录失败的原因
http.csrf().disable();
// 开启注销功能 - 删除cookie、之类的... 一般跳到首页或者登录界面
// logoutSuccessUrl - 注销成功跳转url
// http.logout().logoutSuccessUrl("/login");
// 开启记住我功能,cookie 存活时间 2周。 自定义接收参数
// http.rememberMe().rememberMeParameter("remenber");
// 异常处理 - 403 权限不够
http.exceptionHandling()
.accessDeniedHandler(myAccessDeniedHandler);
}
// 认证
// @Override
// protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//
// // 密码加密与匹配密码
// BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
// String result = encoder.encode("myPassword");
// System.out.println(encoder.matches("myPassword", result));
//
// // 这些用户数据应该从数据库中获取
// /*
// password 这部分,必须要进行加密方式处理。 passwordEncoder(new BCryptPasswordEncoder())
// 否则后台报错 There is no PasswordEncoder mapped for the id "null"
// */
// auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
// .withUser("zhangsan").password(new BCryptPasswordEncoder().encode("123456")).roles("admin", "user")
// .and()
// .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("admin");
// }
}
UserDetailsService
这个接口package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
/**
* <p>
* 自定义登录逻辑
* </p>
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
PasswordEncoder passwordEncoder;
// 模拟的后端数据
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 这个 username 是表单中的 username -> 可以通过配置更改为 pwd 等其他
System.out.println("自定义登录逻辑,前端传参用户名:"+username);
// User user = userService.findByUserName(username);
// System.out.println("自定义登录逻辑,获取到的用户:"+user);
// 1.查询数据库判断用户是否存在,不存在则抛异常 UsernameNotFoundException
if ("user".equals(username)) {
// 2.存在用户 -> 获取密码(注册时已经加密过的密码,进行解析) 或者 直接把密码放入构造方法
String password = passwordEncoder.encode("123");
// admin 是权限 ROLE_abc 是角色
return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin, ROLE_abc"));
} else {
throw new UsernameNotFoundException("用户不存在");
}
}
}
package com.example.demo.hanlder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private String url;
public MyAuthenticationSuccessHandler(String url) {
this.url = url;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
System.out.println("自定义登录认证成功处理");
User user = (User) authentication.getPrincipal();
System.out.println("登录成功,用户信息:" + user.toString());
response.sendRedirect(url);
}
}
package com.example.demo.hanlder;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
private String url;
public MyAuthenticationFailureHandler(String url) {
this.url = url;
}
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
System.out.println("自定义登录认证失败处理");
response.sendRedirect(url);
}
}
package com.example.demo.hanlder;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* <p>
* 自定义处理 403 方案
* </p>
*/
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
System.out.println("自定义权限不够,403处理方案");
// 响应码
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setHeader("Content-Type", "application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write("模拟权限不够界面:权限不够!");
writer.flush();
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
登录错误界面
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
<link rel="stylesheet" type="text/css" href="login.css"/>
<script type="text/javascript" src="login.js"></script>
</head>
<body>
<div id="login_frame">
<form method="post" action="/loogin">
<p><label >用户名</label><input type="text" name="user" /></p>
<p><label >密码</label><input type="text" name="pwd"/></p>
<button type="submit">登录</button>
</form>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
主界面main
</body>
</html>