1. spring security简介
Spring Security 是 Spring 家族中的一个安全管理框架,实际上,在 Spring Boot 出现之前,Spring
Security 就已经发展了多年了,但是使用的并不多,安全管理这个领域,一直是 Shiro 的天下。相对于 Shiro,在 SSM/SSH 中整合 Spring Security 都是比较麻烦的操作,所以,Spring Security
虽然功能比 Shiro 强大,但是使用反而没有 Shiro 多(Shiro 虽然功能没有 Spring Security
多,但是对于大部分项目而言,Shiro 也够用了)。自从有了 Spring Boot 之后,Spring Boot 对于 Spring Security 提供了 自动化配置方案,可以零配置使用
Spring Security。因此,一般来说,常见的安全管理技术栈的组合是这样的:
SSM + Shiro
Spring Boot/Spring Cloud + Spring Security
springsecurity的核心功能主要包括
- 认证(当前用户)
- 授权(当前用户拥有那些个功能)
- 攻击防护(csrf 防止截取cookie值伪造身份登录)
其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic Authentication Filter 用来认证用户的身份,一个在spring security中一种过滤器处理一种认证方式。
2.配置
引 入 p o m 依 赖 \color{#FF3030}{引入pom依赖} 引入pom依赖
<!--spring-security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
配 置 s p r i n g s e c u r i t y \color{red}{配置springsecurity} 配置springsecurity
package com.example.config;
import com.example.handle.MyAccessDeniedHandler;
import com.example.handle.MyAuthenticationFailureHandler;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.sql.DataSource;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyAccessDeniedHandler myAccessDeniedHandler;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private DataSource dataSource;
@Autowired
private PersistentTokenRepository persistentTokenRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
//关闭csrf防护
http.csrf().disable();
http.formLogin()
//当发现是login登录页时必须和表单提交的地址一样,去执行userdetilsserviceimple
.loginProcessingUrl("/login")
//自定义登陆界面
.loginPage("/login.html")
//登陆成功后跳转的页面,必须是post请求
.successForwardUrl("/toMain")
// .failureForwardUrl("/toError")
// .successHandler(new MyAuthenticationSuccessHandler("http://www.baidu.com"))
.failureHandler(new MyAuthenticationFailureHandler("error.html"));
http.authorizeRequests()
//login.html 不需要被认证
.antMatchers("/login.html", "/error.html", "/druid/**").permitAll()
// .regexMatchers(HttpMethod.POST,"/demo").permitAll()
//所有请求必须被认证,必须登陆之后被访问
.anyRequest().authenticated();
//异常处理
http.exceptionHandling()
.accessDeniedHandler(myAccessDeniedHandler);
//记住我
http.rememberMe()
//设置失效时间
.tokenValiditySeconds(60)
//自定义登录逻辑
.userDetailsService(userDetailsService)
//持久层对象
.tokenRepository(persistentTokenRepository);
}
//对密码进行加密处理
@Bean
public PasswordEncoder getPw() {
return new BCryptPasswordEncoder();
}
@Bean
public PersistentTokenRepository getPersistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
//将用户登陆的信息存入数据库中
//自动建表第一次启动需要 第二次关闭
// jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
}
数
据
库
中
存
入
的
用
户
登
录
信
息
\color{red}{数据库中存入的用户登录信息}
数据库中存入的用户登录信息
配 置 用 户 登 录 逻 辑 \color{red}{配置用户登录逻辑} 配置用户登录逻辑
package com.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private PasswordEncoder pw;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("username===========>"+username);
//密码 和 用户名是从数据库中查出来然后进行比较,然后还需查看当前用户有哪些权限
SysUserEntity user = userService.getUserByUserName(username);
if (user == null) {
throw new UsernameNotFoundException(String.format("%s.这个用户不存在", username));
}else {
//查找角色
List<String> roles = roleService.getRolesByUserName(username);
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
System.out.println("loadUserByUsername......user ===> " + user);
return new AuthUser(user.getUserName(), user.getPassWord(), user.getState(), authorities);
}
自定义AuthUser类
/**
* 要实现UserDetails接口,这个接口是security提供的
*/
@Component
public class AuthUser implements UserDetails {
private String username;
private String password;
private Integer state;
private Collection<? extends GrantedAuthority> authorities;
public AuthUser() {
}
public AuthUser(String username, String password, Integer state, Collection<? extends GrantedAuthority> authorities) {
this.username = username;
this.password = password;
this.state = state;
this.authorities = authorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
// 账户是否未过期
@Override
public boolean isAccountNonExpired() {
return true;
}
// 账户是否未被锁
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public Integer getState() {
return state;
}
public void setState(Integer state) {
this.state = state;
}
@Override
public String toString() {
return "JwtUser{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", state=" + state +
", authorities=" + authorities +
'}';
}
}
自定义MyAccessDeniedHandler
package com.example.handle;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Configuration
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("权限不足,请联系管理员");
writer.flush();
writer.close();
}
}
自定义MyAuthenticationFailureHandle 登录失败跳转页面
package com.example.handle;
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 e) throws IOException, ServletException {
response.sendRedirect(url);
}
}
自定义MyAuthenticationFailureHandler 登录成功跳转页面
package com.example.handle;
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 e) throws IOException, ServletException {
response.sendRedirect(url);
}
}