<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2.SpringSecurity完整流程
其中UsernamePasswordAuthenticationFilter负责处理页面填写了用户名密码后的登陆请求,验证是否正确,FilterSecurityInterceptor负责权限校验的过滤器,在没有Filter前后都可以加自定义拦截器。
3.自定义配置
@EnableWebSecurity
public class WebConfigUtil extends WebSecurityConfigurerAdapter {
@Autowired
MyWebAuthenicationDetailsSource detailsSource;
@Autowired
MyUserDetailsAuthenticationProvider provider;
@Autowired
MyJdbcUserDetailService myJdbcUserDetailService;
@Override
protected void configure(HttpSecurity http) throws Exception {
//URL 拦截注册器,获取所有请求 ,antMatchers()和regexMatchers()
http.authorizeRequests()
.antMatchers("/hello").hasAnyRole("xxz")//验证/hello接口必须拥有xxz权限
.antMatchers("/word").hasRole("xx")
.anyRequest().authenticated()//除permitAll()配置的接口,其他所有请求必须认证
.and()
.formLogin()//声明了需要Spring Security提供的表单认证,httpBasic()
// 第二种方式:验证验证码,通过改写认证方式,默认DaoAuthenticationProvider,还需要配置 第四步
.authenticationDetailsSource(detailsSource)//自定义封装form表单参数,如验证码
.loginPage("/login.html")//指定登录页,获取resources/static/
.loginProcessingUrl("/login")//前端展示路径,successHandler 处理登录请求的
.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\":100}");
}
})
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, AuthenticationException e) throws
IOException, ServletException {
}
})
.failureUrl("")
.permitAll()//无权限认证
.and()
.rememberMe().userDetailsService(myJdbcUserDetailService)//添加自动登录功能,默认简单散列加密
.key("key-cookie") //在cookie是一个map类型,如果不知道key,是个UUID随机值
.tokenRepository(new JdbcTokenRepositoryImpl())//基于数据库做持久化
.and()
.logout()//注销登录
.logoutUrl("/mylogout")//非业务代码中真实存在的路径
.logoutSuccessUrl("/")//注销成功,重定向到该路经下
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
}
})
.invalidateHttpSession(true)//使其用户HttpSession失效
.deleteCookies()//删除cookie
.and()
.sessionManagement().sessionFixation().none()//一共4种模式
.invalidSessionUrl("")//会话过期跳转路由,或者会话过期策略
.maximumSessions(1)//最多可以同时登录一个
.maxSessionsPreventsLogin(true)//是否剔除旧的登录,true阻止停止旧登录
.and()
.and()
.csrf().disable();//跨站请求伪造防护功能
//第一种方式验证验证码:在验证密码前添加自定义的拦截器
http.addFilterBefore(new MyFilter(), UsernamePasswordAuthenticationFilter.class);
// super.configure(http);//必须注释掉
}
4.实现WebSecurityConfigurerAdapter 第二个方法
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//第一种添加用户方式
auth.inMemoryAuthentication().withUser("xx").password("oo").roles();
// 第二种方式:验证验证码,配合上面自定义容器使用
auth.authenticationProvider(provider);//自定义认证方式,可以实现多余的验证方式
}
5.用户管理方式
//第四步提供了一种方式,多种方式可以选择一种,数据来源可以是内存,也可以是数据库
/**
* 第二种方式:基于内存
* @return
*/
@Bean
public UserDetailsService userDetailsService2(){
InMemoryUserDetailsManager manager =new InMemoryUserDetailsManager();
User user =new User("a",
new BCryptPasswordEncoder().encode("1"),true,
true,true,true, Collections.singleton(new SimpleGrantedAuthority("xx")));
manager.createUser(user);
manager.createUser(User.withUsername("huang")
.password(new BCryptPasswordEncoder().encode("xx")).roles("xxz").build());
return manager;
}
/**
* 第三种方式:基于默认数据库,org\springframework\security\core\userdetails\jdbc\users.ddl
*
* @return
*/
//@Bean
public UserDetailsService userDetailsService3(){
JdbcUserDetailsManager manager =new JdbcUserDetailsManager();
manager.createUser(User.withUsername("huang")//从数据库取出放入内存中,进行校验
.password(new BCryptPasswordEncoder().encode("xx")).roles("xxz").build());
return manager;
}
/**
* 第四种方式:定义User对象:继承UserDetails, isAccountNonExpired、isAccountNonLocked 和 isCredentialsNonExpired 统一返回 true
* 自定义的User要实现equels和hashCode方法,为了注册销毁内存中的对象
* 继承UserDetailsService中
* @return
*/
//为第六步
6.基于自定义的数据库表创建的
public class MyUser implements UserDetails {
private Long id;
private String username;
private String password;
private String roles;
private boolean enable;
private List<GrantedAuthority> authorities;
}
@Service
public class MyJdbcUserDetailService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
// 实现从数据库读取用户信息
MyUser dto=new MyUser();
// 多权限用;分割
dto.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(dto.getRoles()));
return null;
}
}
7.自定义认证过程
比如在验证用户密码前,验证验证码是否正确,还有一种方式是自定义Filter
//补充用户提交的验证和session保持的验证码信息
public class MyWebAuthenticationDetails extends WebAuthenticationDetails {
private boolean imageCodeIsRight;
public boolean getImageCodeIsRight(){
return this.imageCodeIsRight;
}
public MyWebAuthenticationDetails(HttpServletRequest request) {
super(request);
// 在这一步对用户提交的表单信息,获取,并与用户session中的信息比较,如短信验证码,并将比对结果放入
this.imageCodeIsRight=true;
}
}
@Component
public class MyUserDetailsAuthenticationProvider extends DaoAuthenticationProvider {
public MyUserDetailsAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
this.setUserDetailsService(userDetailsService);
this.setPasswordEncoder(passwordEncoder);
}
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
// 先完成完成图形验证
super.additionalAuthenticationChecks(userDetails, authentication);
}
}
@Component
public class MyUserDetailsAuthenticationProvider extends DaoAuthenticationProvider {
public MyUserDetailsAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
this.setUserDetailsService(userDetailsService);
this.setPasswordEncoder(passwordEncoder);
}
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
// 先完成完成图形验证
MyWebAuthenticationDetails details= (MyWebAuthenticationDetails) authentication.getDetails();
//将之前的结果取出
details.getImageCodeIsRight();
super.additionalAuthenticationChecks(userDetails, authentication);
}
}
8.密码加密方式选择,针对数据库非明文存储
//是否加密认证:NoOpPasswordEncoder 不加密
//BCryptPasswordEncoder 非对称加密
@Bean
public PasswordEncoder passwordEncoder(){
//return NoOpPasswordEncoder.getInstance();
return new BCryptPasswordEncoder();
}
9.application.properties配置
#会话过期时间 server.servlet.session.timeout= 60
<html>
<head>
<meta charset="utf-8">
</head>
<body>
登录
<form action="/login" method="post">
用户名:<input name="username" type="text"><br>
密码: <input name="password" type="password">
<br>
<!-- 验证码<input name="code" type="text"> <img src="http://localhost/kaptcha">-->
<br>
<!-- <input th:name="${_csrf.parameterName}" th:value="${_csrf.token}"> -->
<button type="submit" >登录</button>
</form>
</body>
</html>
浙公网安备 33010602011771号