SpringSecurity-认证和授权
表单登录
单体应用
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
//5.0以后默认使用非明文密码,如下方式使用已经过时的明文密码验证
@Bean
PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() //返回一个url拦截注册器,通过他来匹配需要拦截的请求
.anyRequest().authenticated() //所有请求都要被拦截进行认证
.and()//回到HttpSecurity http
.ignoring().antMatchers("js/**","css/**")//静态自愿放行
.and()
.formLogin() //返回一个SpringScurity提供的表单认证配置器
.loginPage("/myLogin.html") //指定自定义登录页,同时会以/myLogin.html注册一个Post路由,用于接收登录请求
.loginProcessingUrl("/login")//代替默认注册的验证路由,使用自定义路由接收登陆请求
.successForwardUrl("/index.html")//登录成功的跳转页面
.failureForwardUrl("/error.html")//登录失败的跳转页面
.usernameParameter("user")//替换默认接收用户名参数(username)为user
.passwordParameter("pwd")//替换默认接收密码参数(password)为pwd
.permitAll()//对操作路由进行放行
.and()
.csrf().disable();//跨站请求伪造防护功能,当继承WebSecurityConfigurerAdapter时会默认开启csrf()方法
}
}
前后端分离应用
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() //返回一个url拦截注册器,通过他来匹配需要拦截的请求
.anyRequest().authenticated() //所有请求都要被拦截进行认证
.and()//回到HttpSecurity http
.formLogin() //返回一个SpringScurity提供的表单认证配置器
.loginPage("/myLogin.html") //指定自定义登录页,同时会以/myLogin.html注册一个Post路由,用于接收登录请求
.loginProcessingUrl("/login")//代替默认注册的验证路由,使用自定义路由接收登陆请求
.successForwardUrl("/index.html")//登录成功的跳转页面
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=UTF-8");
PrintWriter out = httpServletResponse.getWriter();
out.write("{\"error_code\":\"0\",\"message\":\"登录成功\"}");
}
})//登录成功返回json数据
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=UTF-8");
PrintWriter out = httpServletResponse.getWriter();
out.write("{\"error_code\":\"401\",\"message\":\"登录验证失败\"}");
}
})//登录失败返回json数据
.usernameParameter("user")//替换默认接收用户名参数(username)为user
.passwordParameter("pwd")//替换默认接收密码参数(password)为pwd
.permitAll()//对操作路由进行放行
.and()
.csrf().disable();//跨站请求伪造防护功能,当继承WebSecurityConfigurerAdapter时会默认开启csrf()方法
}
}
授权
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() //返回一个url拦截注册器,通过他来匹配需要拦截的请求
.anyRequest().authenticated() //所有请求都要被拦截进行认证
.antMatchers("/admin/api/**").hasAnyRole("ADMIN")//admin角色用户可以访问限制
.antMatchers("/user/api/**").hasAnyRole("USER")//user角色用户访问限制
.antMatchers("/app/api/**").permitAll()//所有角色用户都可访问
.and()//回到HttpSecurity http
.formLogin() //返回一个SpringScurity提供的表单认证配置器
.loginPage("/myLogin.html") //指定自定义登录页,同时会以/myLogin.html注册一个Post路由,用于接收登录请求
.loginProcessingUrl("/login")//代替默认注册的验证路由,使用自定义路由接收登陆请求
.successForwardUrl("/index.html")//登录成功的跳转页面
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=UTF-8");
PrintWriter out = httpServletResponse.getWriter();
out.write("{\"error_code\":\"0\",\"message\":\"登录成功\"}");
}
})//登录成功返回json数据
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=UTF-8");
PrintWriter out = httpServletResponse.getWriter();
out.write("{\"error_code\":\"401\",\"message\":\"登录验证失败\"}");
}
})//登录失败返回json数据
.usernameParameter("user")//替换默认接收用户名参数(username)为user
.passwordParameter("pwd")//替换默认接收密码参数(password)为pwd
.permitAll()//对操作路由进行放行
.and()
.csrf().disable();//跨站请求伪造防护功能,当继承WebSecurityConfigurerAdapter时会默认开启csrf()方法
}
}
antMatchers()是一个采用ANT模式的URL匹配器。
- 匹配任意单个字符 *
- 匹配任意多个字符 **
认证和授权
基于内存的多用户支持
实现自定义的UserDetailsService,任何实现UserDetailsService接口的对象都可以作为验证数据源
InMemoryUserDetailsManager是UserDetailsService接口中的一个实现类,它将用户数据源寄存在内存里,这里仅仅调用createUser()生成两个用户,并赋予相应的角色。
@Bean
public UserDetailsService userDetailsService(){
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
inMemoryUserDetailsManager.createUser(User.withUsername("liuhui").password("12121").roles("ADMIN").build());
inMemoryUserDetailsManager.createUser(User.withUsername("zhujie").password("12121").roles("USER").build());
return inMemoryUserDetailsManager;
}
基于默认数据库模型的多用户支持
JdbcUserDetailsManager帮助我们以JDBC的方式对接数据库和Spring Security,它设定了一个默认的数据库模型。
JdbcUserDetailsManager需要两个表,User表和authorities表,用来描述角色和权限的关联
Users:用户名、密码、是否可用
authorities:用户名、权限
相对于基于内存的方式很相似,主要区别是相当于把用户信息保存到了数据库中,比如creatUser相当于在数据库中执行
insert into users(username,password,enabled) values(?,?,?)
@Autowired
private DataSource dataSource;
@Bean
public UserDetailsService userDetailsService(){
JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager();
jdbcUserDetailsManager.setDataSource(dataSource);
jdbcUserDetailsManager.createUser(User.withUsername("liuhui").password("12121").roles("ADMIN").build());
jdbcUserDetailsManager.createUser(User.withUsername("zhujie").password("12121").roles("USER").build());
return jdbcUserDetailsManager;
}
基于自定义数据库模型的认证和授权
编写一个用户实体实现UserDetails
public class User implements UserDetails {
private Long id;//用户id
private String username;//用户名
private String password;//密码
private String roles;//权限信息
private boolean enbled;//是否启用
private List<GrantedAuthority> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return null;
}
@Override
public boolean isAccountNonExpired() { //是否过期
return true;
}
@Override
public boolean isAccountNonLocked() { //是否锁定
return true;
}
@Override
public boolean isCredentialsNonExpired() { //用户凭证是否过期
return true;
}
@Override
public boolean isEnabled() {
return enbled;
}
//set//get
编写通过用户名查找用户的mapper
public interface UserMapper {
@Select("SELECT * FROM users WHERE username=#{username}")
User findByUserName(@Param("username") String username);
}
编写MyUserDetailsService并实现UserDetailsService
@Service
public class MyUserDetailService implements UserDetailsService {
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//从数据库尝试读取该用户
User user = userMapper.findByUserName(username);
//用户不存在,抛出异常
if(user == null){
throw new UsernameNotFoundException("用户不存在!");
}
//将数据库形式的roles解析为UserDetails的权限集
//AuthorityUtils.commaSeparatedStringToAuthorityList是SpringSecurity提供的,该方法用于将逗号隔开的权限集字符串切割成可用权限集合
//也可自己实现
user.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRoles()));
return user;
}
}