Spring Security是一个Java框架,用于保护应用程序的安全性。它提供了一套全面的安全解决方案,包括身份验证、授权、防止攻击等功能。Spring Security基于过滤器链的概念,可以轻松地集成到任何基于Spring的应用程序中。它支持多种身份验证选项和授权策略,开发人员可以根据需要选择适合的方式。此外,Spring Security还提供了一些附加功能,如集成第三方身份验证提供商和单点登录,以及会话管理和密码编码等。总之,Spring Security是一个强大且易于使用的框架,可以帮助开发人员提高应用程序的安全性和可靠性。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
<version>2.1.7.RELEASEversion>
dependency>
要从数据库读取用户信息进行身份认证,需要新建类实现UserDetailService接口重写loadUserByUsername方法:
package com.lin.security.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lin.security.entity.Users;
import com.lin.security.mapper.UsersMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("userDetailsService")
public class MyUserDateilsService implements UserDetailsService {
@Autowired
private UsersMapper usersMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
/* 根据用户名从数据库中查找用户信息,此处结合mybatis plus查询*/
QueryWrapper<Users> wrapper = new QueryWrapper();
wrapper.eq("username",s);
Users users = usersMapper.selectOne(wrapper);
if (users == null){
throw new UsernameNotFoundException("用户不存在");
}
/* 把用户的权限加到auths中返回,权限可存储到数据库中,从数据库中查询后添加带auths*/
/* 测试权限*/
/* List auths = AuthorityUtils.commaSeparatedStringToAuthorityList("user"); */
/* 测试角色,需要添加前缀ROLE_,无ROLE_则为权限 */
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_user");
/*返回用户名、密码(加密后)、权限;*/
/*对从数据库中查询出的密码进行加密,
new BCryptPasswordEncoder().encode(users.getPassword()),
建议:推荐把密码加密后再存储到数据库,此处就无需加密操作
*/
return new User(users.getUsername(),new BCryptPasswordEncoder().encode(users.getPassword()),auths);
}
}
创建Security的配置类WebSecurityConfig继承WebSecurityConfigurerAdapter,并重写configure(auth)方法:
package com.lin.security.config;
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.authentication.builders.AuthenticationManagerBuilder;
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 SecurityConfigTest extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)/*从数据库读取的用户进行身份认证*/
.passwordEncoder(passwordEncoder());/*加密方式*/
}
/*对角色的权限——所能访问的路径做出限制*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 退出登录,退出后跳转路径 /test/hello
http.logout().logoutUrl("/logout").logoutSuccessUrl("/test/hello").permitAll();
/** 自定义403页面 */
http.exceptionHandling().accessDeniedPage("/unauth.html");
http.formLogin() //自定义登录页面
.loginPage("/login.html")//登录页面设置
.loginProcessingUrl("/test/login")//登录访问路径
.defaultSuccessUrl("/test/index").permitAll()//登录成功之后,跳转路径
.and().authorizeRequests()
.antMatchers("/test/hello").permitAll()
// 测试权限,当前登录用户,只有具有admin权限才可以访问这个路径,hasAuthority方法
// .antMatchers("/test/index").hasAuthority("admin")
// .antMatchers("/test/index").hasAnyAuthority("admin,user")
// 测试角色
.antMatchers("/test/index").hasRole("user")
.anyRequest().authenticated()
/** 自动登录功能 rememberMe() 记住我 */
.and().rememberMe().tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(60) //设置有效时长,60S
.userDetailsService(userDetailsService)
.and().csrf().disable(); //关闭csrf防护
}
/*configure(WebSecurity)用于影响全局安全性
(配置资源,设置调试模式,通过实现自定义防火墙定义拒绝请求)的配置设置。
一般用于配置全局的某些通用事物,例如静态资源等*/
/*public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}*/
/**加密方式*/
@Bean
PasswordEncoder passwordEncoder(){
/*使用BCrypt加密密码,可自定义加密方式*/
return new BCryptPasswordEncoder();
}
}
附加:Spring Security 设置2种加密方式(强散列哈希加密、自定义加密)
PasswordEncoder
/**
* 身份认证接口
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
/*从数据库读取的用户进行身份认证*/
.userDetailsService(userDetailsService)
/*加密方法(强散列哈希加密)*/
.passwordEncoder(new BCryptPasswordEncoder());
}
package org.sang.config;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
/**
* 自定义加密方式
*/
@Component
public class MyPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes());
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encodedPassword.equals(DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes()));
}
}
Spring Security设置加密方式
/**
* 身份认证接口
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
/*从数据库读取的用户进行身份认证*/
.userDetailsService(userDetailsService)
/*加密方法(自定义加密方式)*/
.passwordEncoder(new MyPasswordEncoder());
}
不加密方式
package com.entity;
import cn.hutool.core.util.StrUtil;
import org.springframework.security.crypto.password.PasswordEncoder;
public class CustomPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return StrUtil.equals(charSequence, s);
}
}
package com.lin.security.controller;
import com.lin.security.entity.Users;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.access.prepost.PreFilter;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/test")
public class test {
@GetMapping("/hello")
public String hello(){
return "spring Security";
}
@PostMapping("/login")
public String login(){
return "this your login";
}
@GetMapping("/index")
public String index(){
return "this your spring Security";
}
@GetMapping("/update")
// @Secured({"ROLE_user","ROLE_person"})
// 单个权限认证
// @PreAuthorize("hasAuthority('admins')")
// 多个权限认证
// @PreAuthorize("hasAnyAuthority('admins,person')")
// 单个角色认证
// @PreAuthorize("hasRole('ROLE_user')")
// 多个角色认证
// @PreAuthorize("hasAnyRole('ROLE_user,Role_admin')")
public String update(){
return "update";
}
@GetMapping("/list")
@PostFilter(value = "filterObject.username=='libai'")
public List<Users> list(){
ArrayList<Users> list = new ArrayList<Users>();
list.add(new Users(1,"libai","666"));
list.add(new Users(2,"dufeng","123"));
return list;
}
@GetMapping("/info")
@PreFilter(value = "filterObject.id%2==0")
public List<Users> info(@RequestBody List<Users> list){
list.forEach(t->{
System.out.println(t.getUserid()+"\t"+t.getUsername());
});
return list;
}
}
package com.lin.security.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Users {
private Integer userid;
private String username;
private String password;
}
详情可参考文章