spring security是安全框架,最常使用的是认证和授权,认证是登录操作,授权是针对请求访问的限制。
在项目开发中正常情况下,认证和授权需要自定义一些功能
认证
登录逻辑(实现UserDetailsService接口)
从数据库中获取数据
@Component public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; @Autowired private UserMapper userMapper; @Autowired private RoleMapper roleMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //根据username获取user信息 com.marw.sys.entity.User user=userMapper.findUserByUsername(username); if(user==null){ throw new UsernameNotFoundException("用户不存在"); } user.setPassword(passwordEncoder.encode(user.getPassword())); //根据UserID获取菜单 List<RoleExtendMap> menuList = roleMapper.findRoleByUsreId(user.getId()); SecurityUser securityUser = new SecurityUser(user, menuList); return securityUser; } }
授权
登录前操作(UsernamePasswordAuthenticationFilter过滤器中attemptAuthentication方法)
获取表单提交的用户名和密码
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (this.postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } else { String username = this.obtainUsername(request); username = username != null ? username : ""; username = username.trim(); String password = this.obtainPassword(request); password = password != null ? password : ""; UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); this.setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } }
登录成功操作,登录失败操作(在UsernamePasswordAuthenticationFilter父类
AbstractAuthenticationProcessingFilter过滤器中successfulAuthentication、unsuccessfulAuthentication方法)
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { SecurityContextHolder.getContext().setAuthentication(authResult); if (this.logger.isDebugEnabled()) { this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult)); } this.rememberMeServices.loginSuccess(request, response, authResult); if (this.eventPublisher != null) { this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass())); } this.successHandler.onAuthenticationSuccess(request, response, authResult); } protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)
throws IOException, ServletException { SecurityContextHolder.clearContext(); this.logger.trace("Failed to process authentication request", failed); this.logger.trace("Cleared SecurityContextHolder"); this.logger.trace("Handling authentication failure"); this.rememberMeServices.loginFail(request, response); this.failureHandler.onAuthenticationFailure(request, response, failed); }
自定义登录前、登录成功和登录失败操作就可以继承UsernamePasswordAuthenticationFilter并重写这三个方法
授权设置(BasicAuthenticationFilter过滤器中doFiler)
授权的本质:权限信息交给权限上下文
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { try { UsernamePasswordAuthenticationToken authRequest = this.authenticationConverter.convert(request); if (authRequest == null) { this.logger.trace("Did not process authentication request since failed to find username and password in Basic Authorization header"); chain.doFilter(request, response); return; } String username = authRequest.getName(); this.logger.trace(LogMessage.format("Found username '%s' in Basic Authorization header", username)); if (this.authenticationIsRequired(username)) { Authentication authResult = this.authenticationManager.authenticate(authRequest); //权限信息存储在权限上下文中
SecurityContextHolder.getContext().setAuthentication(authResult); if (this.logger.isDebugEnabled()) { this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult)); } this.rememberMeServices.loginSuccess(request, response, authResult); this.onSuccessfulAuthentication(request, response, authResult); } } catch (AuthenticationException var7) { SecurityContextHolder.clearContext(); this.logger.debug("Failed to process authentication request", var7); this.rememberMeServices.loginFail(request, response); this.onUnsuccessfulAuthentication(request, response, var7); if (this.ignoreFailure) { chain.doFilter(request, response); } else { this.authenticationEntryPoint.commence(request, response, var7); } return; } chain.doFilter(request, response); }
未授权(实现AuthenticationEntryPoint接口)
public class Http403ForbiddenEntryPoint implements AuthenticationEntryPoint { private static final Log logger = LogFactory.getLog(Http403ForbiddenEntryPoint.class); public Http403ForbiddenEntryPoint() { } public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2) throws IOException { logger.debug("Pre-authenticated entry point called. Rejecting access"); response.sendError(403, "Access Denied"); } }
SpringSecurity配置类
作用:设置认证和授权
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsServiceImpl;//自定义认证操作 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //指定认证和加密方式 auth.userDetailsService(userDetailsServiceImpl).passwordEncoder(passwordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http.exceptionHandling().authenticationEntryPoint(new 自定义未授权类) .addFilter(new 自定义过滤器);//可以实现认证前后操作和授权数据的设置 } @Bean public PasswordEncoder passwordEncoder(){ //return new BCryptPasswordEncoder(); //自定义MD5加密 return new MD5PasswordEncoder(); } }