Spring Boot使用Spring Security遇到的问题

SpringSecurity从数据库中取数据授权验证

定制首页,从数据库中直接取数据校验遇到的一些坑。

首先引入依赖Spring Security的依赖,省略前置铺垫…

1. ajax请求被拦截

ajax请求发送成功,返回错误代码403。
解决方案1:
在html头添加:

	<meta name="_csrf" th:content="${_csrf.token}"/>
	<meta name="_csrf_header"  th:content="${_csrf.headerName}"/>

在javascript添加:

	var header = $("meta[name='_csrf_header']").attr("content");
	var token =$("meta[name='_csrf']").attr("content");

在ajax的方法中添加:

	$.ajax({
    
    
    	beforeSend : function(xhr) {
    
    
        	xhr.setRequestHeader(header, token);
        },
	});

解决方案2:
在Security配置文件,授权方法中添加:

	http.csrf().disable();

2.实现从数据库中取数据后校验

login表
login表
role表
这里role表添加了'ROLE_'前缀是参考了网上一位老哥的博文,框架内部的角色有前缀。
role表

中间表
中间表

  • POJO类继承User或实现UserDetails

    • 继承User需要调用父类的构造器

    • 实现UserDetails需要覆盖默认方法,提供权限,并将各项返回值设为true

      @Override
      	public Collection<? extends GrantedAuthority> getAuthorities() {
              
              
      		List<SimpleGrantedAuthority> authorities = new ArrayList<>();
      		roles.forEach((item) -> {
              
              
      			authorities.add(new SimpleGrantedAuthority(item.getRoleName()));
      		});
      		return authorities;
      	}
      
      	@Override
      	public String getPassword() {
              
              
      		return this.loginPassword;
      	}
      
      	@Override
      	public String getUsername() {
              
              
      		return this.loginAccount;
      	}
      
      	@Override
      	public boolean isAccountNonExpired() {
              
              
      		return true;
      	}
      
      	@Override
      	public boolean isAccountNonLocked() {
              
              
      		return true;
      	}
      
      	@Override
      	public boolean isCredentialsNonExpired() {
              
              
      		return true;
      	}
      
      	@Override
      	public boolean isEnabled() {
              
              
      		return true;
      	}
      
  • 业务层需要实现UserDetailsService接口,并实现方法

    • 在Security5后需要自定义加密
      在Security配置中写了就可以省略

      	@Bean
      	PasswordEncoder passwordEncoder() {
              
              
      		return new BCryptPasswordEncoder();
      	}
      
    • 方法的具体实现

      	@Override
      	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
              
              
      		Login login = dao.queryLoginByAccount(username);
      		if (login != null) {
              
              
      			List<Role> roles = dao.queryRoleListByLoginId(login.getLoginId());
      			login.setRoles(roles);
      			login.setLoginPassword(new BCryptPasswordEncoder().encode(login.getPassword()));
      		} else {
              
              
      			throw new UsernameNotFoundException("用户名不存在");
      		}
      		return login;
      	}
      
  • 在Security配置中调用业务层

    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
          
          
    
    	@Autowired
    	private LoginServiceImpl loginService;
    
    	@Override
    	protected void configure(HttpSecurity http) throws Exception {
          
          
    		// 授权
    		http.authorizeRequests()
      			.antMatchers("/", "/user/toLogin").permitAll()
      			.antMatchers("/user/home").hasRole("user")
      			.antMatchers("/user/home", "/test/**").hasRole("admin")
      			.and()
      			.formLogin()
      			.loginPage("/user/toLogin")
      			.loginProcessingUrl("/login")
      			.successForwardUrl("/user/home")
      			.permitAll();
      		// 如果不关闭csrf,则ajax将会被拦截
      		http.csrf().disable();
    	}
    
    	@Override
    	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          
          
    		auth.userDetailsService(loginService)
    			.passwordEncoder(new BCryptPasswordEncoder());
    	}
    }
    

3.定制登录页

表单中的action一定要写成thymeleaf中的形式,并与Security配置中的url一致。
<input />中的name字段是固定的,如果想自定义可以在Security中自己配置(大概)。

   <!DOCTYPE html>
   <html lang="en" xmlns:th="http://www.thymeleaf.org">
   <head>
       <meta charset="UTF-8">
       <title>Title</title>
   </head>
   <body>
       <form th:action="@{/login}" method="post">
           <input type="text" name="username" placeholder="账号" /><br>
           <input type="password" name="password" placeholder="密码" /><br>
           <input name="submit" type="submit" value="提交">
       </form>
   </body>
   </html>

4.数据库存储(额外)

由于上面这种配置模式,需要根据传入的username去数据库中进行查找,不能传入用户名密码传入到SQL语句中直接查询,所以只能取到加密的密码,之后在service层进行组装后与前端校验。而密码又不能明文存储在数据库中,所以使用了Mysql的加密解密方法,在查找的时候使用,直接取到密码明文。
加密:AES_ENCRYPT('password','key')
解密:AES_DECRYPT(password, 'key')

猜你喜欢

转载自blog.csdn.net/x805111268/article/details/109755676