Spring Security http接口保护、方法保护示范项目以及配置方式、注解方式示范
Spring Security
安全认证主要分为这么几个部分
第一个是用户,也就是用来登录的账号
第二个是角色,不同的网址、资源可能需要是某些角色才能访问
第三个就是授权,对于别人提供的登录凭证(账号密码),你要鉴定并在鉴定通过后给别人授权。
第四个就是配置哪些地方需要什么权限。
被权限管理的可以是网址、函数等,常用的就是网址的访问、函数的调用
默认登录地址/login ,默认的退出登录地址/logout,有默认的页面在
maven依赖:
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--已经有这个就不需要加了-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
项目资源地址:https://download.csdn.net/download/HumorChen99/15501057(免费下载)
用户
就是你要从来登录的账号和密码
角色
- 比如用户角色、管理员角色、DBA角色等等,我们用一个字符串来代指
- new SimpleGrantedAuthority(“ROLE_USER”)
- new SimpleGrantedAuthority(“ROLE_ADMIN”)
- new SimpleGrantedAuthority(“ROLE_DBA”)
授权器(从提供的账号密码来授权)
- 基于内存的身份验证
说白了就是直接把账号密码还有对应的角色直接写在了代码里配置好,账号密码匹配上了就有这个权限,不太常用,看一眼就算了,一般用作系统内置账号
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
- JDBC验证、LDAP验证这里不说,目前对我不常用
实现AuthenticationProvider接口的自定义身份验证(常用)
就是把别人提交的账号密码传入到这个函数来,你自己进行鉴定,并在鉴定通过后返回权限,找不到账号或者不归你这个管则直接返回null,spring security会继续依次调用每一个授权器去尝试授权,直到成功。
其中授予权限的时候有个点要注意,我是遇到了的
在后续配置的时候hasRole和hasAuthority是要区分好的,hasRole的权限是 ROLE_ 开头的,而hasAuthority可以不用这个前缀,
举例说明:
如果你后面配置某网址需要USER权限的时候用hasRole(“USER”)的话你就需要在授权的时候授予new SimpleGrantedAuthority(“ROLE_USER”),需要加 ROLE_ 前缀
而如果比配置的时候使用的hasAuthority(“GET”),则授权的时候直接授予new SimpleGrantedAuthority(“GET”)即可。
有了下面三个类以后,我们运行访问/user的时候由于没有GET权限会被拦截掉,去登录界面(登录界面可以自己写,post提交地址为/login,不写则用默认的一个登录界面),登录成功后你会获得你应该有的权限
UserAuthenticationProvider 类:授权器
@Component
public class UserAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//拿到提交的账号和密码
String username=authentication.getName();
String password=authentication.getCredentials().toString();
System.out.println("得到的账号:"+username+" 密码:"+password);
//自己的账号密码判定逻辑
if(username.equals("admin")&&password.equals("admin")){
Collection<GrantedAuthority> authorities=new ArrayList<>();
//授予 GET 权限
authorities.add(new SimpleGrantedAuthority("GET"));
//创建用户 UserDetails对象(用户名,密码,权限集合)
UserDetails user=new User(username,password,authorities);
return new UsernamePasswordAuthenticationToken(user,password,authorities);
}
return null;
}
@Override
public boolean supports(Class<?> aClass) {
return aClass.equals(UsernamePasswordAuthenticationToken.class);
}
}
SecurityConfiguration 类:安全配置类
有多个configure函数你可以根据需要去复写这些函数配置不同的东西
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private UserAuthenticationProvider userAuthenticationProvider;
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
//将授权器加入到授权器管理器里去(多个的时候依次调用鉴定授权,直到某个授权了)
auth.authenticationProvider(userAuthenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/","/error").permitAll().antMatchers("/user").hasAuthority("GET")
.and().formLogin().defaultSuccessUrl("/user").and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/");
}
}
UserController 类:做接口
@RestController
public class UserController {
@GetMapping("/user")
public String user(){
Authentication authentication= SecurityContextHolder.getContext().getAuthentication();
Collection<GrantedAuthority> authorities=(Collection<GrantedAuthority>)authentication.getAuthorities();
if(authorities!=null){
System.out.println("这个账号有的授权是:");
System.out.println(authorities.toString());
return authorities.toString();
}
return "没有授权";
}
@GetMapping("/")
public String home(){
return "index";
}
@RequestMapping("/login")
public void login(){
}
}
登录后转到了/user效果图:
实现UserDetailsService接口 根据账号名从数据库读取账号密码权限交给spring security去判定(常用)
PasswordEncoderConfig 类:密码加密器
@Configuration
public class PasswordEncoderConfig {
@Bean
public BCryptPasswordEncoder getPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}
UserDetailsServiceImpl 类:授权器
这里我就不查数据库了,代指一下意思意思,看注释自然懂,当spring security把客户端传过来的密码使用配置里的密码加密器加密后和你提供的密码进行对比,一致则授予你返回的那个权限集合
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名username查数据库拿正确密码(spring security会自己去判断密码是不是正确)
//前端提交的是密码明文,这边给spring security的要是密码的被加密后的值,且需要配置加密器
//这里我直接写个123456并加密123456把密文传给spring security
String password="123456";
password=passwordEncoder.encode(password);
System.out.println("被加密后的密码为"+password);
Collection<GrantedAuthority> authorities=new ArrayList<>();
//查这个用户如果密码正确拥有什么角色(可多个)
authorities.add(new SimpleGrantedAuthority("GET"));
//user有两个构造方法
UserDetails user=new User(username,password,authorities);
return user;
}
}
SecurityConfiguration 类:配置类
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
//将授权器加入到授权器管理器里去(多个的时候依次调用鉴定授权,直到某个授权了)
//由于userDetailsService方式完成授权的话从数据库里查出来的密码是被加密过的,而密码对比是由spring security来做的,所以你需要指定密码加密器
//而且这里你是可以配置多个授权器的,spring security会依次的去鉴权,鉴权成功则给你应有的权限。否则继续试下一个直到成功或者最终失败
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/","/error").permitAll().antMatchers("/user").hasAuthority("GET")
.and().formLogin().defaultSuccessUrl("/user").and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/login.html");
}
}
controller类还是上面那个现成的,测试方法一样(记得先退出登录)
方法鉴权(基于注解)
项目资源地址:https://download.csdn.net/download/HumorChen99/15501053(免费下载)
MethodSecurityConfig 类:做配置
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
/**
* 设置加密方式为 BCrypt强hash
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 关闭 csrf()
http.authorizeRequests().anyRequest().authenticated().and().formLogin().loginProcessingUrl("/user");
}
}
PasswordEncoderConfig 类:密码加密器
/**
* 密码加密器配置
* @author humorchen
*/
@Configuration
public class PasswordEncoderConfig {
@Bean
public BCryptPasswordEncoder getPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}
UserDetailsServiceImpl 类
/**
* UserDetailServiceImpl
* 这是一个服务,传入用户名,返回用户名,正确的密码,这个账号的权限,然后由spring security去给你判断密码是不是正确,正确就赋予权限
* @author humorchen
*/
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名查数据库拿正确密码(spring security会自己去判断密码是不是正确)
String password="123456";
password=passwordEncoder.encode(password);
System.out.println("被加密后的密码为"+password);
Collection<GrantedAuthority> authorities=new ArrayList<>();
//查这个用户如果密码正确拥有什么角色(可多个)
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
//user有两个构造方法
UserDetails user=new User(username,password,authorities);
return user;
}
}
UserController 类
@RestController
public class UserController {
@Autowired
private TestService testService;
@GetMapping("/user")
public String user(){
Authentication authentication= SecurityContextHolder.getContext().getAuthentication();
Collection<GrantedAuthority> authorities=(Collection<GrantedAuthority>)authentication.getAuthorities();
if(authorities!=null){
System.out.println("这个账号有的授权是:");
System.out.println(authorities.toString());
return authorities.toString();
}
return "没有授权";
}
@GetMapping("/secured_test")
public String secured_test(){
return testService.getStrSecured();
}
@GetMapping("/pre_auth_test")
public String pre_auth_test(){
return testService.getStrPreAuth();
}
@RequestMapping("/login")
public void login(){
}
}
TestService 类
@Service
public class TestService {
/**
* 授权的时候需要授予 ROLE_USER
* 经过测试,使用@Secured注解的必须带有ROLE_前缀
* @return
*/
@Secured("ROLE_USER")
public String getStrSecured(){
return "getStrSecured";
}
/**
* 授权的时候需要授予 ROLE_USER
* 经过测试,授权ROLE_USER后@PreAuthorize("ROLE_USER")和@PreAuthorize("USER")均可以通过鉴权
* @return
*/
@PreAuthorize("ROLE_USER")
public String getStrPreAuth(){
return "getStrPreAuth";
}
}
账号admin密码123456
访问:
http://localhost/user 查看当前角色
http://localhost/secured_test 测试访问被@Secured注解保护的接口
http://localhost/pre_auth_test 测试访问被@PreAuthorize注解保护的接口
http://localhost/logout 退出登录
本文来自博客园,作者:HumorChen99,转载请注明原文链接:https://www.cnblogs.com/HumorChen/p/18039657