文章目录
前言
至于什么是Spring security ,主要用户认证和授权。即我们常说的,用户只有登录了才能进行其他操作,没有登录的话就重定向到登录界面。有的用户有权限执行某一操作,而有的用户不能执行则是授权。算是一个项目安全框架。和shiro 框架一样。二者的不同大家可以百度下。Spring security 是Spring家族的一员,所以Springboot算是对Spring security 进行的天然的支持,今天学习了下,做个总结
一、添加maven依赖
<!--Security框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</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>
<scope>test</scope>
</dependency>
<!--thymeleaf与Spring Security整合的依赖 https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
server:
port: 8080
servlet:
encoding:
charset: utf-8
enabled: true
spring:
servlet:
multipart:
maxFileSize: 102400000
maxRequestSize: 102400000
thymeleaf:
prefix: classpath:/templates/
suffix: .html
encoding: UTF-8
cache: false # 禁用缓存
mode: HTML5 # 非严格型检查 默认是html 严格检查
servlet:
content-type: text/html
项目结构
二、创建SecurityConfig.java配置类
代码如下
mport com.longWind.common.security.handle.MyAccessDeniedHandler;
import com.longWind.common.security.handle.MyAuthenticationFailureHandler;
import com.longWind.common.security.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @author wanglei
* @date 2021年05月12日 21:27
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyAccessDeniedHandler deniedHandler;
@Autowired
private UserDetailsServiceImpl userDetailsService;
// @Autowired
// private PersistentTokenRepository persistentTokenRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
//表单提交
http.formLogin()
//设置登录页面from表单的用户名,密码的别名
// 用户名:<input type="text" name="username123"/><br/>
// 密码:<input type="password" name="password123"/><br/>
// .usernameParameter("username123")
// .passwordParameter("password123")
//拦截登录请求
.loginProcessingUrl("/login")
//自定义登录页面
.loginPage("/showLogin")
//登录成功后跳转页面,post请求
.successForwardUrl("/toMain")
//登录成功后的处理器不能和successForwardUrl共存
// .successHandler(new MyAuthenticationSuccessHandler("/main.html"))
// .failureForwardUrl("/toError")
//登录失败的处理器不能和failureForwardUrl共存
.failureHandler(new MyAuthenticationFailureHandler("/error.html"))
;
//授权认证
http.authorizeRequests()
//login.html不需要认证
// .antMatchers("/login.html").permitAll()
.antMatchers("/showLogin").access("permitAll()")
// .antMatchers("/error.html").permitAll()
//所有的都可以用access来实现
.antMatchers("/error.html").access("permitAll()")
//放行静态资源
.antMatchers("/image/**","/js/**","/css/**").permitAll()
//正则表达式放行png结尾的静态资源
// .regexMatchers(".+[.]png").permitAll()
//指定请求方式做拦截
//.regexMatchers(HttpMethod.POST,".toDome").permitAll()
// .mvcMatchers("/toDome").servletPath("/xxx").permitAll()
//权限严格区分大小写,拥有admin权限才能访问/main1.html
// .antMatchers("/main1.html").hasAnyAuthority("admin")
//拥有admin,admiN等配置的任何一个权限都可以访问/main1.html
// .antMatchers("/main1.html").hasAnyAuthority("admin,admiM")
//拥有abc角色可以访问/main.html
// .antMatchers("/main1.html").hasRole("abcd")
// .antMatchers("/main1.html").access("hasRole('abc')")
//拥有abc,abcd等配置的任何一个角色都可以访问/main.html
// .antMatchers("/main1.html").hasAnyRole("abc,abcd")
//只有IP地址为127.0.0.1才能访问/main1.html
// .antMatchers("/main1.html").hasIpAddress("127.0.0.1")
//所有请求都被拦截,必须登录后才能访问
.anyRequest().authenticated ()
//自定义实现url权限控制,
// .anyRequest().access("@myServiceImpl.hasPermission(request,authentication)")
;
//关闭csrf防护
// http.csrf().disable();
//异常处理 自定义权限不足处理
http.exceptionHandling().accessDeniedHandler(deniedHandler);
// //记住我
// http.rememberMe()
// //设置失效时间 单位 秒
//// .tokenValiditySeconds(60)
// //自定义前端登录页面记住我的name值
//// .rememberMeParameter("")
// //自定义登录逻辑
// .userDetailsService(userDetailsService)
// //持久层对象
// .tokenRepository(persistentTokenRepository);
//退出登录跳转页面
http.logout()
//退出成功后的跳转页面
.logoutSuccessUrl("/login.html");
}
// @Bean
// public PersistentTokenRepository getPersistentTokenRepository(){
// JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
// jdbcTokenRepository.setJdbcTemplate(jdbcTemplate);
// //自动建表,第一次启动的时候需要,第二次启动注释
// jdbcTokenRepository.setCreateTableOnStartup(true);
// return new JdbcTokenRepositoryImpl();
// }
@Bean
public PasswordEncoder getPassword()
{
return new BCryptPasswordEncoder();
}
}
三、自定义权限不足处理
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;
/**
* 自定义权限不足处理
* @author wanglei
* @date 2021年05月13日 21:32
*/
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
//设置相应状态码
response.setStatus(HttpServletResponse.SC_FORBIDDEN );
response.setHeader("Content-Type","application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write("{\"status\":\"error\",\"mag\":\"权限不足请联系管理员 \"}");
writer.flush();
writer.close();
}
}
四、自定义登录失败的处理器MyAuthenticationFailureHandler.java
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;
/**
* @author wanglei
* @date 2021年05月13日 13:40
*/
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
private String url;
public MyAuthenticationFailureHandler(String url){
this.url=url;
}
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,AuthenticationException e) throws IOException, ServletException {
response.sendRedirect(url);
}
}
五、自定义登录成功的处理器MyAuthenticationSuccessHandler.java
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author wanglei
* @date 2021年05月13日 13:27
*/
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private String url;
public MyAuthenticationSuccessHandler(String url){
this.url=url;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
System.out.println(request.getRemoteAddr());
User user = (User) authentication.getPrincipal();
System.out.println(user.getUsername());
System.out.println(user.getAuthorities());
System.out.println(user.getPassword());
response.sendRedirect(url);
}
}
五、登录验证逻辑处理类UserDetailsServiceImpl.java
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;
/**
* 自定义登录验证逻辑处理类
* @author wanglei
* @date 2021年05月12日 21:31
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private PasswordEncoder pw;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//1.查询数据库判断用户名是否存在,如果不存在抛出异常UsernameNotFoundException
if (!"admin".equals(username)){
throw new UsernameNotFoundException("用户名不存在");
}
//2.把查询的密码(注册时已经加密)进行解析,或者直接把密码放入构造函数
String password=pw.encode("123");
return new User(username,password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal," +
//ROLE_ 固定写法,配置角色
"ROLE_abc,/main.html"));
}
}
六、access 自定义权限拦截
//自定义实现url拦截
.anyRequest().access("@myServiceImpl.hasPermission(request,authentication)")
import org.springframework.security.core.Authentication;
import javax.servlet.http.HttpServletRequest;
/**
*
* @author wanglei
* @date 2021年05月13日 21:48
*/
public interface MyService {
boolean hasPermission(HttpServletRequest request, Authentication authentication);
}
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
/**
* 自定义权限
* @author wanglei
* @date 2021年05月13日 21:51
*/
@Service
public class MyServiceImpl implements MyService{
@Override
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
Object principal = authentication.getPrincipal();
if (principal instanceof UserDetails){
UserDetails userDetails =(UserDetails)principal;
Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
return authorities.contains(new SimpleGrantedAuthority(request.getRequestURI()));
}
return false;
}
}
测试
LoginController
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 系统授权接口
* @author wanglei
* @date 2021年05月09日 16:33
*/
@Slf4j
@Controller
public class LoginController {
@RequestMapping("/login")
public String login(){
return "redirect:main.html";
}
// @Secured("ROLE_abc")
//PreAuthorize的表达式在方法执行之前做权限判断
// @PreAuthorize("hasRole('abc')")
@RequestMapping("/toMain")
public String toMain(){
return "redirect:main.html";
}
@RequestMapping("/toError")
public String toError(){
return "redirect:error.html";
}
@RequestMapping("/toDome")
public String toDome(){
return "dome";
}
@RequestMapping("/showLogin")
public String showLogin(){
return "login";
}
}
login.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--自定义登录页面-->
<form action="/login" method="post">
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
记住我:<input type="checkbox" name="remember-me" value="true"/><br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>
main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
登录成功!!!!!!!!!!!!!!!
<a href="/logout">退出</a>
<a href="/main1.html">跳转</a>
</body>
</html>
main1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
main1
</body>
</html>
权限测试
deome.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>springboot-thymeleaf demo</title>
</head>
<body>
<!--自定义登录页面-->
<form action="/login" method="post">
<input type="hidden" th:value="${_csrf.token}" name="_csrf" th:if="${_csrf}">
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
记住我:<input type="checkbox" name="remember-me" value="true"/><br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>
login.html _csrf关闭csrf测试
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>springboot-thymeleaf demo</title>
</head>
<body>
<!--自定义登录页面-->
<form action="/login" method="post">
<input type="hidden" th:value="${_csrf.token}" name="_csrf" th:if="${_csrf}">
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
记住我:<input type="checkbox" name="remember-me" value="true"/><br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>