三种用户认证方式
1. 配置文件
spring.security.user.name=admin
spring.security.user.password=admin
2. 配置类
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//密码加密
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String password = passwordEncoder.encode("admin");
//配置用户名和密码认证
auth.inMemoryAuthentication().withUser("admin").password(password).roles("admin");
}
@Bean
public PasswordEncoder getPasswordEncoder(){
//获取PasswordEncoder
return new BCryptPasswordEncoder();
}
}
3. UserDetailsService实现类
- 配置类:
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());
}
@Bean
public PasswordEncoder getPasswordEncoder(){
// 获取PasswordEncoder
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login.html")//登录的页面
.loginProcessingUrl("/user/login")//登录的请求路径
.defaultSuccessUrl("/test/index").permitAll()//登录成功之后的路径
.and().authorizeRequests()
//设置哪些路径可以直接访问,不需要访问
.antMatchers("/","/test/hello","/user/login").permitAll()
.anyRequest().authenticated()
.and().csrf().disable();//关闭crsf防护
}
}
- 实现类:
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//用户权限
List<GrantedAuthority> auths =
AuthorityUtils.commaSeparatedStringToAuthorityList("role");
return new User("admin",
new BCryptPasswordEncoder().encode("admin"), auths);
}
}
四种角色控制方式
- hasAuthority():单个角色控制
- hasAnyAuthority():多个角色控制,符合其一即可
- hasRole():单个角色控制
- hasAnyRole():多个角色控制,符合其一即可
设置403页面
- 当用户没有角色权限时,页面会报错403
- 配置类设置403报错页面
常用注解
- @Secured
- 判断是否具有角色权限,匹配的字符串需要添加前缀“ROLE_角色权限”
- 首先在配置类或者启动类上添加注解@EnableGlobalMethodSecurity(securedEnable = true)
@RequestMapping("secured")
@ResponseBody
@Secured({"ROLE_sale","ROLE_admin"})
public String secured() {
System.out.println("---secured---");
return "hello secured";
}
- @PreAuthorize
- 进入方法之前进行角色权限认证
- 首先在配置类或者启动类上添加注解@EnableGlobalMethodSecurity(prePostEnabled = true)
@RequestMapping("/preAuthorize")
@ResponseBody
@PreAuthorize("hasRole('ROLE_sale')")
public String preAuthorize(){
//没权限不进入方法
System.out.println("---preAuthorize---");
return "hello preAuthorize";
}
- @PostAuthorize
- 方法执行之后再判断用户的角色权限
- 首先在配置类或者启动类上添加注解@EnableGlobalMethodSecurity(securedEnable = true)
@RequestMapping("/postAuthorize")
@ResponseBody
@PostAuthorize("hasRole('ROLE_sale')")
public String postAuthorize(){
//先执行方法再判断权限
System.out.println("---postAuthorize---");
return "hello postAuthorize";
}
- @PostFilter
- 根据设定的条件将返回值过滤
@RequestMapping("postFilter")
@PreAuthorize("hasRole('ROLE_sale')")
@PostFilter("filterObject.username == 'admin1'")
@ResponseBody
public List<Users> postFilter(){
System.out.println("---postFilter---");
ArrayList<Users> list = new ArrayList<>();
list.add(new Users(1,"admin1","admin1"));
list.add(new Users(2,"admin2","admin2"));
System.out.println(list);
return list;//只返回过滤条件的数据
}
- PreFilter
- 根据过滤条件将传入参数进行过滤
@RequestMapping("preFilter")
@PreAuthorize("hasRole('ROLE_sale')")
@PreFilter("filterObject.username == 'admin1'")
@ResponseBody
public List<Users> preFilter(@RequestBody List<Users> list){
System.out.println("---preFilter---");
//只收到过滤条件的数据参数
System.out.println(list);
return list;
}
退出操作
//退出操作
http.logout()
.logoutUrl("/logOut")//退出的请求路径
.logoutSuccessUrl("/test/hello").permitAll();//退出后跳转的请求页面
自动登录
- 页面勾选框name只能是remember-me,否则无法识别到勾选框
<input type="checkbox" name="remember-me">自动登录</input>
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private DataSource dataSource;//注入数据源
@Bean
public PersistentTokenRepository getPersistentTokenRepository(){
//获取自动登录的配置实现类
JdbcTokenRepositoryImpl jdbcTokenRepository=new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
//自动创建表persistent_logins
//jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());
}
@Bean
public PasswordEncoder getPasswordEncoder(){
// 获取PasswordEncoder
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//登录表单提交
http.formLogin()
.loginPage("/login.html")//登录的页面
.loginProcessingUrl("/user/login")//登录的请求路径
.defaultSuccessUrl("/success.html").permitAll()//登录成功之后的路径
.and().authorizeRequests()
//设置哪些路径可以直接访问,不需要访问
.antMatchers("/", "/test/hello", "/user/login").permitAll()
//当前登录用户,只有具有admins权限才可以访问这个路径
//1 hasAuthority方法
// .antMatchers("/test/index").hasAuthority("admins")
//2 hasAnyAuthority方法
// .antMatchers("/test/index").hasAnyAuthority("admins,manager")
//3 hasRole方法 ROLE_sale
.antMatchers("/test/index").hasRole("sale")
.anyRequest().authenticated()
.and().rememberMe()//自动登录设置
.tokenRepository(getPersistentTokenRepository())
.tokenValiditySeconds(60)//记住用户60秒
.userDetailsService(userDetailsService)
.and().csrf().disable();//关闭crsf防护
//403异常跳转页面
http.exceptionHandling().accessDeniedPage("/unAuth.html");
//退出操作
http.logout()
.logoutUrl("/logOut")//退出的请求路径
.logoutSuccessUrl("/test/hello").permitAll();//退出后跳转的请求页面
}
}
CSRF
1. 简单理解
- 跨站请求伪造(Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。
- 跟跨网站脚本(XSS)相比,XSS利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
- web 中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
- 从Spring Security 4.0 开始,默认情况下会启用CSRF 保护,以防止CSRF 攻击应用程序,Spring Security CSRF 会针对 PATCH,POST,PUT 和DELETE 方法进行防护。
2. 具体实现
- 关闭csrf配置,默认开启
http.csrf().disable()
- 登录页面添加隐藏域
<input type="hidden"
th:if="${_csrf}!=null"
th:name="${_csrf.parameterName}"
th:value="${_csrf.token}"/>
- 收到请求时,从请求中提取 csrfToken,和保存的 csrfToken 做比较,进而判断当前请求是否合法。主要通过 CsrfFilter 过滤器来完成。