Security总结
- 加入依赖
- 配置WebSecurityConfig(3个重写)
加入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
配置WebSecurityConfig
WebSecurityConfig继承WebSecurityConfigurerAdapter,需要重写以下三个方法
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//定义用户(user )、 密码(password )和角色 ( role )
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//指定用户和角色与对应 URL 的访问权限
}
@Override
public void configure(WebSecurity web) throws Exception {
//配置 Filter 链的内容,可以配置 Filter 链忽略哪些内容
}
AuthenticationManagerBuilder
通常自定义CustomAuthenticationProvider处理用户名,密码和角色;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
CustomAuthenticationProvider继承AbstractUserDetailsAuthenticationProvider类,主要重写以下方法:
public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
@Autowired
private CustomUserDetailsService customUserDetailsService;
/**
* 校验密码有效性.
*
* @param userDetails 用户正确的密码 .
* @param authentication .传过来的账户密码
* @throws AuthenticationException .
*/
@Override
protected void additionalAuthenticationChecks(
UserDetails userDetails, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
String value = (String)authentication.getCredentials();
System.out.println("additionalAuthenticationChecks"+value);
/*
判断userDetails 和 authentication是否一致
*/
}
/**
* 获取用户.
* .
* @param authentication .
* @return
* @throws AuthenticationException .
*/
@Override
protected UserDetails retrieveUser(
String wxCode, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
System.out.println("retrieveUser "+ wxCode);
UserDetails loadedUser = customUserDetailsService.loadUserByUsername(wxCode);
if (loadedUser == null) {
throw new InternalAuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
/**
* 授权持久化.
*/
@Override
protected Authentication createSuccessAuthentication(Object principal,
Authentication authentication, UserDetails user) {
return super.createSuccessAuthentication(principal, authentication, user);
}
additionalAuthenticationChecks用来校验数据库存储用户UserDetail和传来的用户是否一致;retrieveUser则是需要根据用户名去查找数据库对应用户 返回一个UserDetails;
@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private WxUserService wxUserService;
@Autowired
private RoleService roleService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Collection<GrantedAuthority> authorities = new ArrayList<>();
String sessionId = username;// 我们保存的是sessionId
// 从数据库中取出用户信息
WxUser user = wxUserService.getWxUser(sessionId);
if(user == null){
log.error("{} 不存在SessionId", sessionId);
throw new AuthenticationServiceException(JsonUtils.obj2String(new ErrorInfo(101, sessionId)));
}
// 添加权限
Role role = roleService.selectById(user.getRole());
authorities.add(new SimpleGrantedAuthority(role.getName()));
// 返回UserDetails实现类
return new org.springframework.security.core.userdetails.User(user.getOpenId(), user.getUsername(), authorities);
}
}
HttpSecurity http
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/static/**","/wx/none/**","/security/sign", "/test/**").permitAll()
.anyRequest().authenticated() // 其余的所有请求都需要验证
.and().exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/security/logout")).logoutSuccessHandler(customLogoutSuccessHandler)
.and().csrf().disable() // 禁用CSRF
;
//用重写的Filter替换掉原有的UsernamePasswordAuthenticationFilter
http.addFilterAt(customAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
}
@Bean
JsonUsernamePasswordAuthenticationFilter customAuthenticationFilter() throws Exception {
JsonUsernamePasswordAuthenticationFilter filter = new JsonUsernamePasswordAuthenticationFilter();
filter.setFilterProcessesUrl("/security/login");
filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
filter.setAuthenticationFailureHandler(customAuthenticationFailHandler);
//这句很关键,重用WebSecurityConfigurerAdapter配置的AuthenticationManager,不然要自己组装AuthenticationManager
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
其中:http对应的方法有如下方法
我们自定义如下验证规则:
public class JsonUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
UsernamePasswordAuthenticationToken authRequest;
//attempt Authentication when Content-Type is json
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(messages.getMessage("8000", "非POST请求"));
} else {
String sessionId = request.getParameter("openId");
if(sessionId != null && sessionId.equals("")){
throw new AuthenticationServiceException(messages.getMessage("8001", "openId为空"));
}
// 就是我们要的Authentication
authRequest = new UsernamePasswordAuthenticationToken(sessionId, "");
this.setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
}
自定义登陆成功 登陆失败 退出 无权限访问页面
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws ServletException, IOException {
response.setContentType("application/json;charset=UTF-8");
String sessionId = request.getSession().getId();
log.info("-------SessionId------- {} ", sessionId);
// 登录成功后,返回json数据
response.getWriter().append(JsonUtils.obj2String(JsonData.buildSuccess(200, sessionId)));
}
}
@Component
public class CustomAuthenticationFailHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=UTF-8");
// 登录成功后,返回json数据
httpServletResponse.getWriter().append(JsonUtils.obj2String(JsonData.buildError(500, "登陆失败", e.getMessage())));
}
}
@Component
public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
Authentication authentication) throws IOException, ServletException {
if (authentication != null && authentication.getDetails() != null) {
try {
httpServletRequest.getSession().invalidate();
//you can add more codes here when the user successfully logs out,
//such as updating the database for last active.
} catch (Exception e) {
e.printStackTrace();
}
}
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
}
}
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().append(JsonUtils.obj2String(JsonData.buildError(403, "无权限")));
}
}
WebSecurity web
@Override
public void configure(WebSecurity web) throws Exception {
// 设置拦截忽略文件夹,可以对静态资源放行
web.ignoring().antMatchers("/css/**", "/js/**");
}