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表
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')