SpringSecurity连接数据库实现登录认证
首先,使用SpringSecurity
前需要知道的一点是:要想使用SpringSecurity
必须继承WebSecurityConfigurerAdapter
父类(重写两个configure
方法),SpringSecurity
中进行认证需要实现UserdetailService
接口,把用户名和密码写死的内存中认证为以下写法:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws
Exception {
auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder())
.withUser("user").password(new //密码之所以需要MyPasswordEncoder是因为SpringSecurity要求密码必须有加密方法
MyPasswordEncoder().encode("000")).roles("USER")//用户名user,密码000
.and()
.withUser("admin").password(new
MyPasswordEncoder().encode("123")).roles("ADMIN","USER");//用户名admin,密码123
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin").hasRole("ADMIN")
.antMatchers("/index").access("hasRole('ADMIN') or
hasRole('USER')")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll()
.and()
.csrf()
.disable();
}
}
以上链式写法其实已经将UserdetailServic
实现并加入了Spring
容器进行管理。
下面介绍SpringSecurity
连接数据库,通过数据库中用户名和密码进行认证:
1. 继承WebSecurityConfigurerAdapter
类,并重写方法:
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsServiceImpl userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new HashPasswordEncoder());//这里密码加密要Hash,和存入后台数据库的Hash值比较
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.antMatchers("/index").access("hasRole('USER') or hasRole('ADMIN')")
.anyRequest()
.authenticated()
.and()
.formLogin()
.defaultSuccessUrl("/index")
.and()
.logout()
.permitAll()
.invalidateHttpSession(true)
.clearAuthentication(true)
.and()
.csrf()
.disable()
.rememberMe();
}
}
2. 实现UserDetailsService
接口,实现认证逻辑(无论是内存数据、还是数据库源数据)
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
UserService userService;
MyPasswordEncoder passwordEncoder=new MyPasswordEncoder();
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
User user = userService.findUserByName(userName);
if (user == null) {
return null;
}
Collection<GrantedAuthority> authorities = new ArrayList<>();
String roleName = userService.findUserRoleByName(userName).getRoleName();
if ("admin".equals(roleName)) {
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
} else {
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
}
org.springframework.security.core.userdetails.User userdetail = new org.springframework.security.core.userdetails.User(user.getUserName(),passwordEncoder.encode(user.getPassword()),authorities);
System.out.println("管理员信息:"+user.getUserName()+" "+passwordEncoder.encode(user.getPassword())+" "+userdetail.getAuthorities());//通过客户端输入的用户名查询,若数据库有该用户名则查出该用户名对应的Hash密码,然后进行原字符加密(加密后字符不变),与客户端输入密码Hash后比较
return userdetail;
}
}
这里可能注意到了,有一个MyPasswordEncoder
类,上面其实已经提到过,这是一个提供字符串加密功能的类,SpringSecurity
要求密码必须加密后再进行比对认证。因此必须有一个实现PasswordEncoder
的子类进行加密。
3. 实现PasswordEncoder接口的MyPasswordEncoder类:
public class MyPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return s.equals(charSequence.toString());
}
}
这里这个加密方法加密后相当于返回原字符串,因为数据库的密码已经是Hash之后的字符串,这里就直接和客户端输入的密码Hash之后进行比较就可以了。
4. 实现PasswordEncoder接口的HashPasswordEncoder类:
public class HashPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence charSequence) {
return Md5Utils.code( charSequence.toString());
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return s.equals(Md5Utils.code(charSequence .toString()));
}
}
Md5Utils
加密类可以参看这篇文章
5. 不同角色进入不同默认页面
定义认证成功后自定义的处理器:
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
/**
* 注意:要认证用户成功后才会调用这个方法
*/
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
String role = null;
//项目根地址
String path = request.getContextPath();
//主机+端口号+项目根路径
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
UserDetails principal =(UserDetails) authentication.getPrincipal();
//权限是我手动赋予的,每一个认证成功的用户只有一个权限
Collection<? extends GrantedAuthority> authorities = principal.getAuthorities();
for (GrantedAuthority authority:authorities) {
//所以authorities的size为1,直接赋予
role = authority.getAuthority();
}
//通过不同的角色转到不同的页面
if("ROLE_ADMIN".equals(role)) {
response.sendRedirect(basePath+"admin");//管理员重定向到这个controller接口
} else {
response.sendRedirect(basePath+"index");//用户重定向到这个接口
}
}
还要在WebSecurityConfig中的configure方法中配置登录
.formLogin()
.loginPage("/login")
.successHandler(new LoginSuccessHandler())
.permitAll()