引入依赖
首先我们先导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
导入依赖后启动项目,会有一个默认的登录页面http://localhost:8080/login.html
默认的username为user,password打印在控制台中。
在浏览器中输入账号和密码后会显示项目中login.html页面内容。我们可以自定义自己的登录界面账号密码,用户权限等。
自定义登录界面
实现自定义登录,主要是实现接口UserDetailsService ,返回值UserDetails是一个接口
public interface UserDetails extends Serializable{
Collection<? extends GrantedAuthority> getAuthorities(); // 获取所有权限
String getPassword(); //获取密码
String getUsername(); // 获取用户名
boolean isAccountNonExpired(); // 是否账号过期
boolean isAccountNonLocked(); // 账号是否被锁
boolean isCredentialsNoExpired(); // 凭证(密码)是否过期
boolean isEnabled(); 是否可用
}
返回的User类只能用spring security自带的User类,导包注意别导错org.springframework.security.core.userdetails.User
User的构造函数
public User(String username, String password
,Collection<? extends GrantedAuthority> getAuthorities){
this(username,password,true,true,true,true,getAuthorities);
}
getAuthorities通常都是通过AuthorityUtils.commaSeparatedStringToAuthorityList(“”)来创建authorities集合对象的。参数是一个字符串,多个权限和角色使用逗号分隔。
密码解析器
Spring Security要求容器中必须有PasswordEncoder实例。所以当自定义登录逻辑时要求必须给容器注入PaswordEncoder的bean对象
PaswordEncoder中有的方法:
encode():把参数按照特定的解析规则进行解析。
matches()验证从存储中获取的编码密码与编码后提交的原始密码是否匹配。如果密码匹配,则返回true;如果不匹配,则返回false。第一个参数表示需要被解析的密码。第二个参数表示存储的密码。
upgradeEncoding():如果解析的密码能够再次进行解析且达到更安全的结果则返回true,否则返回false。默认返回false。
这个密码解析器实现的类有很多
通常是使用BCryptPasswordEncoder这个解析器。
下面是登录例子
先搞一个配置类,配置密码解析器
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder getPwdEncoder(){
return new BCryptPasswordEncoder();
}
}
实现UserDetailsService,用来实现对用户登录的操作
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private PasswordEncoder encoder;
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//1. 查询数据库判断用户名是否存在,如果不存在抛出UsernameNotFoundException
xxx.xxx.pojo.User user = userMapper.selecByUserName(username);
if(user == null){
throw new UsernameNotFoundException("用户名不存在");
}
//把查询出来的密码进行解析,或直接把password放到构造方法中。
//理解:password就是数据库中查询出来的密码,查询出来的内容不是123
String password = encoder.encode("123");
// AuthorityUtils.commaSeparatedStringToAuthorityList("admin1,admin2,ROLE_admin3") 是加权限和角色
UserDetails userDetails= User(user.getName(),user.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList("admin1,admin2,ROLE_admin3"));
return userDetails;
}
}
自定义登录页面
需要修改配置类中主要是设置哪个页面是登录页面。
配置类需要继承WebSecurityConfigurerAdapter,并重写configure方法。
只要没有认证通过,就一直会在login页面。
WebSecurityConfigurerAdapter中的一些方法参数:
successForwardUrl()登录成功后跳转地址
loginPage() 登录页面
loginProcessingUrl 登录页面表单提交地址,此地址可以不真实存在。
antMatchers():匹配内容
permitAll():允许
当进行登录时会执行UsernamePasswordAuthenticationFilter过滤器。
usernamePasrameter:账户参数名
passwordParameter:密码参数名
postOnly=true:默认情况下只允许POST请求。
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
// 表单认证
http.formLogin()
//当发现URL时把请求转发给自定义的登录逻辑
.loginProcessingUrl("/login")
// 认证成功后转发的地址。post请求(和客户端<form>)
.successForwardUrl("/showMain") //此处是post请求
// 认证成功后的处理器 或者自定义类实现接口 AuthenticationSuccessHandler
// .successHandle(MyAuthenticationSuccessHandler)
.successHandle(new AuthenticationSuccessHandler(){
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication)IOException, ServletException{
response.sendRedirect("/showMain")
}
})
// 未认证时登录页面的url地址
.loginPage("/showLogin")
//认证失败转发地址
.failureForwardUrl("/showFail")
// 认证失败的处理器
.failureHandler( new AuthenticationFailureHandler){
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication)IOException, ServletException{
response.sendRedirect("/showfail")
}
}
// 客户端用户名参数名称
.usernameParameter("myusername")
// 客户端密码参数名称
.passwordParameter("mypassword");
}
@Bean
public PasswordEncoder getPe(){
return new BCryptPasswordEncoder();
}
}
使用successForwardUrl()时表示成功后转发请求到地址。内部是通过successHandler()方法进行控制成功后交给哪个类进行处理
ForwardAuthenticationSuccessHandler内部就是最简单的请求转发。由于是请求转发,当遇到需要跳转到站外或在前后端分离的项目中就无法使用了。
//自定义认证成功处理器
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
//Principal 主体,存放了登录用户的信息
User user = (User)authentication.getPrincipal();
System.out.println(user.getUsername());
System.out.println(user.getPassword());//密码输出为null
System.out.println(user.getAuthorities());
//重定向到百度。这只是一个示例,具体需要看项目业务需求
httpServletResponse.sendRedirect("http://www.baidu.com");
}
}
失败跟成功一样的
//自定义认证失败处理器
public class MyForwardAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.sendRedirect("/showfail.html");
}
}