1.简介入门
JavaEE和SpringMVC :Spring Security就是通过11个Fliter进行组合管理
小Demo
user实体类
user.type字段,0普通用户,1超级管理员,2版主
补全get set tostring
implement UserDetails,重写以下方法
// true: 账号未过期.
@Override
public boolean isAccountNonExpired() {
return true;
}
// true: 账号未锁定.
@Override
public boolean isAccountNonLocked() {
return true;
}
// true: 凭证未过期.
@Override
public boolean isCredentialsNonExpired() {
return true;
}
// true: 账号可用.
@Override
public boolean isEnabled() {
return true;
}
//获取当前用户权限列表
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> list = new ArrayList<>();
list.add(new GrantedAuthority() {
@Override
public String getAuthority() {
switch (type) {
case 1:
return "ADMIN";
default:
return "USER";
}
}
});
return list;
}
UserService
unservice implement UserDetailsService
重写方法loadUserByUsername
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return this.findUserByName(username);
}
SecurityConfig 配置类
extend父类WebSecurityConfigurerAdapter
注入UserService
重写configure(WebSecurity web),忽略静态资源的访
@Override
public void configure(WebSecurity web) throws Exception {
// 忽略静态资源的访问
web.ignoring().antMatchers("/resources/**");
}
重写configure(AuthenticationManagerBuilder auth),这个方法主要是做认证。
AuthenticationManager: 认证的核心接口
AuthenticationManagerBuilder: 用于构建AuthenticationManager对象的工具
ProviderManager: AuthenticationManager接口的默认实现类
ProviderManager--一组-->AuthenticationProvider--每个负责-->一种认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 内置的认证规则
// auth.userDetailsService(userService).passwordEncoder(new Pbkdf2PasswordEncoder("12345"));
// 自定义认证规则
// AuthenticationProvider: ProviderManager持有一组AuthenticationProvider,每个AuthenticationProvider负责一种认证.
// 委托模式: ProviderManager将认证委托给AuthenticationProvider.
auth.authenticationProvider(new AuthenticationProvider() {
// Authentication: 用于封装认证信息的接口,不同的实现类代表不同类型的认证信息.
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
User user = userService.findUserByName(username);
if (user == null) {
throw new UsernameNotFoundException("账号不存在!");
}
password = CommunityUtil.md5(password + user.getSalt());
if (!user.getPassword().equals(password)) {
throw new BadCredentialsException("密码不正确!");
}
// principal: 主要信息; credentials: 证书; authorities: 权限;
return new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
}
// 当前的AuthenticationProvider支持哪种类型的认证.
@Override
public boolean supports(Class<?> aClass) {
// UsernamePasswordAuthenticationToken: Authentication接口的常用的实现类.
return UsernamePasswordAuthenticationToken.class.equals(aClass);
}
});
}
重写configure(HttpSecurity http)
配置登陆页面http.formLogin()
登录成功处理器.successHandler
登录失败处理器.failureHandler
退出相关配置http.logout()
授权配置http.authorizeRequests()
验证码在验证账号之前
@Override
protected void configure(HttpSecurity http) throws Exception {
// 登录相关配置
http.formLogin()
.loginPage("/loginpage")
.loginProcessingUrl("/login")
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.sendRedirect(request.getContextPath() + "/index");
}
})
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
request.setAttribute("error", e.getMessage());
request.getRequestDispatcher("/loginpage").forward(request, response);
}
});
// 退出相关配置
http.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.sendRedirect(request.getContextPath() + "/index");
}
});
// 授权配置
http.authorizeRequests()
.antMatchers("/letter").hasAnyAuthority("USER", "ADMIN")
.antMatchers("/admin").hasAnyAuthority("ADMIN")
.and().exceptionHandling().accessDeniedPage("/denied");
// 增加Filter,处理验证码
http.addFilterBefore(new Filter() {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if (request.getServletPath().equals("/login")) {
String verifyCode = request.getParameter("verifyCode");
if (verifyCode == null || !verifyCode.equalsIgnoreCase("1234")) {
request.setAttribute("error", "验证码错误!");
request.getRequestDispatcher("/loginpage").forward(request, response);//转发
return;
}
}
// 让请求继续向下执行.
filterChain.doFilter(request, response);
}
}, UsernamePasswordAuthenticationFilter.class);
// 记住我
http.rememberMe()
.tokenRepository(new InMemoryTokenRepositoryImpl())
.tokenValiditySeconds(3600 * 24)
.userDetailsService(userService);
}
HomeController
在首页添加欢迎信息,通过SecurityContextHolder获取登陆者信息
@RequestMapping(path = "/index", method = RequestMethod.GET)
public String getIndexPage(Model model) {
// 认证成功后,结果会通过SecurityContextHolder存入SecurityContext中.
Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (obj instanceof User) {
model.addAttribute("loginUser", obj);
}
return "/index";
}