实现UserDetailsService接口html
@Service public class MyUserDetailsService implements UserDetailsService { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { logger.info("根据用户名查找用户信息,登陆用户名:" + username); // 从数据库查询相关的密码和权限,这里返回一个假的数据 // 用户名,密码,权限 return new User(username, "123456", AuthorityUtils.commaSeparatedStringToAuthorityList("admin")); } }
UserDetails接口的一些方法,封装了登陆时的一些信息java
public interface UserDetails extends Serializable { /** 权限信息 * Returns the authorities granted to the user. Cannot return <code>null</code>. * * @return the authorities, sorted by natural key (never <code>null</code>) */ Collection<? extends GrantedAuthority> getAuthorities(); /** 密码 * Returns the password used to authenticate the user. * * @return the password */ String getPassword(); /** 登陆名 * Returns the username used to authenticate the user. Cannot return <code>null</code> * . * * @return the username (never <code>null</code>) */ String getUsername(); /** 帐户是否过时 * Indicates whether the user's account has expired. An expired account cannot be * authenticated. * * @return <code>true</code> if the user's account is valid (ie non-expired), * <code>false</code> if no longer valid (ie expired) */ boolean isAccountNonExpired(); /** 帐户是否被锁定(冻结) * Indicates whether the user is locked or unlocked. A locked user cannot be * authenticated. * * @return <code>true</code> if the user is not locked, <code>false</code> otherwise */ boolean isAccountNonLocked(); /** 密码是否过时 * Indicates whether the user's credentials (password) has expired. Expired * credentials prevent authentication. * * @return <code>true</code> if the user's credentials are valid (ie non-expired), * <code>false</code> if no longer valid (ie expired) */ boolean isCredentialsNonExpired(); /** 帐户是否可用(删除) * Indicates whether the user is enabled or disabled. A disabled user cannot be * authenticated. * * @return <code>true</code> if the user is enabled, <code>false</code> otherwise */ boolean isEnabled(); }
返回数据写成数据库
return new User(username, // 用户名 "123456", // 密码 true, // 是否可用 true, // 帐号是否过时 true, // 密码是否过时 true, // 帐号没有被锁定标志 AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
PasswordEncoder接口json
public interface PasswordEncoder { /** 加密 * Encode the raw password. Generally, a good encoding algorithm applies a SHA-1 or * greater hash combined with an 8-byte or greater randomly generated salt. */ String encode(CharSequence rawPassword); /** 判断密码是否匹配 * Verify the encoded password obtained from storage matches the submitted raw * password after it too is encoded. Returns true if the passwords match, false if * they do not. The stored password itself is never decoded. * * @param rawPassword the raw password to encode and match * @param encodedPassword the encoded password from storage to compare with * @return true if the raw password, after encoding, matches the encoded password from * storage */ boolean matches(CharSequence rawPassword, String encodedPassword); }
在BrowerSecurityConfig中配置PasswordEncoderapp
// 配置PasswordEncoder @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
MyUserDetailsService.java改为框架
// 注入passwordEncoder @Autowired private PasswordEncoder passwordEncoder; // 返回写成这样 return new User(username, // 用户名 passwordEncoder.encode("123456"), // 这个是从数据库中读取的已加密的密码 true, // 是否可用 true, // 帐号是否过时 true, // 密码是否过时 true, // 帐号没有被锁定标志 AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
修改BrowserSecurityConfig类dom
@Configuration public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter { // 配置PasswordEncoder @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { System.out.println("BrowserSecurityConfig"); http.formLogin() // 表单登陆 .loginPage("/sign.html") // 自定义登陆页面URL .loginProcessingUrl("/authentication/form") // 处理登陆请求的URL .and() .authorizeRequests() // 对请求作受权 .antMatchers("/sign.html").permitAll() // 登陆页面不须要认证 .anyRequest() // 任何请求 .authenticated() // 都须要身份认证 .and().csrf().disable(); // 暂时将防御跨站请求伪造的功能置为不可用 } }
@Configuration public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private SecurityProperties securityProperties; // 配置PasswordEncoder @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { System.out.println("BrowserSecurityConfig"); http.formLogin() // 表单登陆 .loginPage("/authentication/require") // 自定义登陆页面URL .loginProcessingUrl("/authentication/form") // 处理登陆请求的URL .and() .authorizeRequests() // 对请求作受权 .antMatchers("/authentication/require", securityProperties.getBrowser().getLoginPage()) .permitAll() // 登陆页面不须要认证 .anyRequest() // 任何请求 .authenticated() // 都须要身份认证 .and().csrf().disable(); // 暂时将防御跨站请求伪造的功能置为不可用 } }
BrowserSecurityController判断访问的url若是以.html结尾就跳转到登陆页面,不然就返回json格式的提示信息ide
@RestController public class BrowserSecurityController { private Logger logger = LoggerFactory.getLogger(getClass()); private RequestCache requestCache = new HttpSessionRequestCache(); private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); @Autowired private SecurityProperties securityProperties; /** * 须要身份认证时,跳转到这里 * * @param request * @param response * @return */ @RequestMapping("/authentication/require") @ResponseStatus(code = HttpStatus.UNAUTHORIZED) public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException { SavedRequest savedRequest = requestCache.getRequest(request, response); if (savedRequest != null) { String targetUrl = savedRequest.getRedirectUrl(); logger.info("引起跳转请求的url是:" + targetUrl); if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) { redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage()); } } return new SimpleResponse("访问的服务须要身份认证,请引导用户到登陆页"); } }
AuthenticationSuccessHandler接口,此接口登陆成功后会被调用ui
@Component public class ImoocAuthenticationSuccessHandler implements AuthenticationSuccessHandler { private Logger logger = LoggerFactory.getLogger(ImoocAuthenticationSuccessHandler.class); @Autowired private ObjectMapper objectMapper; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { logger.info("登陆成功"); // 登陆成功后把authentication返回给前台 response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(authentication)); } }
@Component public class ImoocAuthenticationFailHandler implements AuthenticationFailureHandler { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ObjectMapper objectMapper; @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { logger.info("登陆失败"); response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(e)); } }
登陆成功后的处理加密
@Component public class ImoocAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { private Logger logger = LoggerFactory.getLogger(ImoocAuthenticationSuccessHandler.class); @Autowired private ObjectMapper objectMapper; @Autowired private SecurityProperties securityProperties; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { logger.info("登陆成功"); if (LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())) { // 登陆成功后把authentication返回给前台 response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(authentication)); } else { super.onAuthenticationSuccess(request, response, authentication); } } }
登陆失败后的处理
@Component public class ImoocAuthenticationFailHandler extends SimpleUrlAuthenticationFailureHandler { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ObjectMapper objectMapper; @Autowired private SecurityProperties securityProperties; @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { logger.info("登陆失败"); if (LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())) { response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(e)); } else { super.onAuthenticationFailure(request, response, e); } } }
一个请求进来的时候,先检查context是否存有该请求的认证信息
验证码图片的宽,高,字符数,失效时间可配置(注意字符数和失效时间不要在请求级配置中)。请求级配置就是在请求验证码时/code/image?width=100&height=30,应用级配置就是在应用的配置文件中
// 在使用这些配置时,若是请求级配置有就用请求级配置,不然就依次用应用级配置,默认配置 int width = ServletRequestUtils.getIntParameter(request.getRequest(), "width", securityProperties.getCode().getImage().getWidth()); int height = ServletRequestUtils.getIntParameter(request.getRequest(), "height", securityProperties.getCode().getImage().getHeight());
默认状况下,只有在注册,登陆的须要验证码的时候才拦截的,若是还有其余情景下须要则可以在不修改依赖的状况下可配置.如何实现呢,在配置文件中添加要须要验证码的url,验证码的验证是经过过滤器实现的,那么在对其过滤的时候判断当前url是不是须要拦截便可
把生成验证码的功能定义成接口,框架给出一个默认的实现,若是应用不定义就用这个默认实现,若是应用要定制一个,就实现这个接口就能够了.
// 框架中的默认实现不加注释@Component进行初始化,用以下方式对其进行初始化 // 检测上下文环境中是否有imageCodeGenerator这个bean,若是没有就初始化框架中提供的默认实现 @Configuration public class ValidateCodeBeanConfig { @Autowired private SecurityProperties securityProperties; @Bean @ConditionalOnMissingBean(name = "imageCodeGenerator") public ValidateCodeGenerator imageCodeGenerator() { System.out.println("init imageCodeGenerator"); ImageCodeGenerator codeGenerator = new ImageCodeGenerator(); codeGenerator.setSecurityProperties(securityProperties); return codeGenerator; } }
@Configuration public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter { // 用来读取配置 @Autowired private SecurityProperties securityProperties; // 登陆成功后的处理 @Autowired private ImoocAuthenticationSuccessHandler imoocAuthenticationSuccessHandler; // 登陆失败后的处理 @Autowired private ImoocAuthenticationFailHandler imoocAuthenticationFailHandler; @Autowired private DataSource dataSource; @Autowired private UserDetailsService userDetailsService; // 配置PasswordEncoder @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } // 用于remember me @Bean public PersistentTokenRepository persistentTokenRepository() { JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl(); tokenRepository.setDataSource(dataSource); // tokenRepository.setCreateTableOnStartup(true); // 启动时建立表 return tokenRepository; } @Override protected void configure(HttpSecurity http) throws Exception { System.out.println("BrowserSecurityConfig"); ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter(); validateCodeFilter.setAuthenticationFailureHandler(imoocAuthenticationFailHandler); validateCodeFilter.setSecurityProperties(securityProperties); validateCodeFilter.afterPropertiesSet(); http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) .formLogin() // 表单登陆 .loginPage("/authentication/require") // 自定义登陆页面URL .loginProcessingUrl("/authentication/form") // 处理登陆请求的URL .successHandler(imoocAuthenticationSuccessHandler) // 登陆成功后的处理 .failureHandler(imoocAuthenticationFailHandler) // 登陆失败后的处理 .and() .rememberMe() .tokenRepository(persistentTokenRepository()) .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds()) .userDetailsService(userDetailsService) .and() .authorizeRequests() // 对请求作受权 .antMatchers("/authentication/require", securityProperties.getBrowser().getLoginPage(), "/code/image") .permitAll() // 登陆页面不须要认证 .anyRequest() // 任何请求 .authenticated() // 都须要身份认证 .and().csrf().disable(); // 暂时将防御跨站请求伪造的功能置为不可用 } }
略