實現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(); // 暫時將防禦跨站請求僞造的功能置爲不可用 } }
略