javascript
Spring Security学习(二)
自定義配置登錄頁面
@Override protected void configure(HttpSecurity http) throws Exception {http.formLogin()// 自定義頁面路徑.loginPage("/api/login").and().authorizeRequests()// 允許/api/login的URL訪問 否則瀏覽器頁面將提示重定向次數過多進入死循環.antMatchers("/api/login").permitAll().anyRequest().authenticated(); }自定義登錄路徑
@Override protected void configure(HttpSecurity http) throws Exception {http.formLogin().loginPage("/api/login")// 自定義登錄路徑.loginProcessingUrl("/api/formlogin") .and().authorizeRequests().antMatchers("/api/login").permitAll().anyRequest().authenticated(); }Spring Security 默認表單登錄
public UsernamePasswordAuthenticationFilter() {super(new AntPathRequestMatcher("/login", "POST")); }可設置loginProcessingUrl屬性來替換默認登錄地址
登錄成功處理
當用戶登錄成功后需要保存用戶登錄數據,比如IP地址,登錄時間等
// 注入SecurityLoginSuccessHandler @Autowired private SecurityLoginSuccessHandler securityLoginSuccessHandler; @Override protected void configure(HttpSecurity http) throws Exception {http.formLogin().loginPage("/api/login")// 自定義登錄成功處理.successHandler(securityLoginSuccessHandler).loginProcessingUrl("/api/formlogin").and().authorizeRequests().antMatchers("/api/login","/static/*").permitAll().anyRequest().authenticated().and().csrf().disable(); }SecurityLoginSuccessHandler
public class SecurityLoginSuccessHandler implements AuthenticationSuccessHandler {// 重定向跳轉類private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();// 緩存請求路徑,可獲取攔截前的請求路徑private RequestCache requestCache = new HttpSessionRequestCache();public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {System.out.println("登錄成功");// 獲取攔截前請求路徑 并通過RedirectStrategy進行重定向跳轉SavedRequest savedRequest = requestCache.getRequest(request, response);String redirectUrl = savedRequest.getRedirectUrl();// todo do somethingthis.redirectStrategy.sendRedirect(request, response, redirectUrl);} }登錄失敗處理
跟登錄成功處理一樣 只是實現接口不同而已
@Autowired private SecurityLoginFailHandler securityLoginFailHandler;@Override protected void configure(HttpSecurity http) throws Exception {http.formLogin().loginPage("/api/login").successHandler(securityLoginSuccessHandler)// 自定義登錄失敗處理.failureHandler(securityLoginFailHandler).loginProcessingUrl("/api/formlogin").and().authorizeRequests().antMatchers("/api/login", "/static/*").permitAll().anyRequest().authenticated().and().csrf().disable(); }SecurityLoginFailHandler
public class SecurityLoginFailHandler implements AuthenticationFailureHandler {public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {System.out.println("登錄失敗");// todo do somethingresponse.setCharacterEncoding("UTF-8");response.setContentType("text/json;charset=UTF-8");response.getWriter().write(exception.getMessage());} }退出登錄
默認是訪問URL/logout將注銷登陸的用戶
自定義配置
@Autowired private SecurityLoginoutHandler securityLoginoutHandler; @Autowired private SecurityLoginoutSuccessHandler securityLoginoutSuccessHandler;@Override protected void configure(HttpSecurity http) throws Exception {http.formLogin().loginPage("/api/login").successHandler(securityLoginSuccessHandler).failureHandler(securityLoginFailHandler).loginProcessingUrl("/api/formlogin").and().authorizeRequests().antMatchers("/api/login", "/static/*").permitAll().anyRequest().authenticated().and().logout()// 退出登錄地址.logoutUrl("/logout")// 退出跳轉路徑.logoutSuccessUrl("/api/login")// 默認session失效.invalidateHttpSession(true)// 退出登錄處理.addLogoutHandler(securityLoginoutHandler)// 退出登錄成功處理.logoutSuccessHandler(securityLoginoutSuccessHandler)// 指定退出登錄后需要刪除的cookie名稱,多個cookie之間以逗號分隔。 // .deleteCookies(cookieNamesToClear).and().csrf().disable(); }SecurityLoginoutHandler
用來執行必要的清理,因而他們不應該拋出錯誤
public class SecurityLoginoutHandler implements LogoutHandler {public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {System.out.println("退出登錄");} }SecurityLoginoutSuccessHandler
被LogoutFilter在成功注銷后調用,用來進行重定向或者轉發相應的目的地。注意這個接口與LogoutHandler幾乎一樣,但是可以拋出異常。
public class SecurityLoginoutSuccessHandler implements LogoutSuccessHandler {private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {System.out.println("退出登錄成功執行");this.redirectStrategy.sendRedirect(request, response, "/");} }自定義用戶登錄賬號密碼
WebSecurityConfig
@Autowired private SecurityUserDetailService securityUserDetailService;@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {// 在內存中寫死用戶數據auth.inMemoryAuthentication().withUser("123456").password("123456").roles("USER");// 動態獲取用戶數據auth.userDetailsService(securityUserDetailService); }SecurityUserDetailService
/*** 自定義UserDetailService** @author Peng*/ public class SecurityUserDetailService implements UserDetailsService {/*** String password; 密碼* String username; 賬戶名* Set<GrantedAuthority> authorities; 角色名* boolean accountNonExpired; 賬戶沒有過期* boolean accountNonLocked; 帳號沒有被鎖定* boolean credentialsNonExpired; 密碼沒有過期* boolean enabled; 是否可用*/public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 這里寫死的密碼 實際應用中應該從數據庫中獲取 User user = new User(username, "123456", true,true,true,true,AuthorityUtils.commaSeparatedStringToAuthorityList("USER"));return user;} }自定義用戶登錄賬號密碼(密碼加密)
WebSecurityConfig
private SecurityUserDetailService securityUserDetailService; public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {// 在內存中寫死用戶數據auth.inMemoryAuthentication().withUser("123456").password("123456").roles("USER");// 動態獲取用戶數據 使用Security加密auth.userDetailsService(securityUserDetailService).passwordEncoder(new BCryptPasswordEncoder());// 配置驗證方式為加密驗證 DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();authenticationProvider.setUserDetailsService(securityUserDetailService);authenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());auth.authenticationProvider(authenticationProvider); }SecurityUserDetailService
/*** 自定義UserDetailService** @author Peng*/ @Component public class SecurityUserDetailService implements UserDetailsService {/*** String password; 密碼* String username; 賬戶名* Set<GrantedAuthority> authorities; 角色名* boolean accountNonExpired; 賬戶沒有過期* boolean accountNonLocked; 帳號沒有被鎖定* boolean credentialsNonExpired; 密碼沒有過期* boolean enabled; 是否可用*/@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 這里寫死的密碼 實際應用中應該從數據庫中獲取 BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();// 數據庫創建用戶數據時密碼應通過Security加密后保存String password = encoder.encode("123456");System.out.println(password);User user = new User(username, password, true,true,true,true,AuthorityUtils.commaSeparatedStringToAuthorityList("USER"));return user;} }PasswordEncoder
// 密碼加密 String encode(CharSequence rawPassword); // 驗證密碼是否吻合 boolean matches(CharSequence rawPassword, String encodedPassword);獲取登錄用戶信息
/*** 獲取登錄用戶數據*/ ("/api/getme") public Object getLoginUser(Authentication authentication) {Map<String, Object> json = new HashMap<>();// 兩種方法json.put("data", SecurityContextHolder.getContext().getAuthentication());json.put("data1", authentication);return json; }資源權限限制
WebSecurityConfig
.antMatchers("/api/me").hasRole("ADMIN")這樣只要是訪問“api/me”的路徑就會驗證用戶身份,用戶身份必須是ADMIN才允許訪問| hasRole([role]) | 如果當前主體具有指定的角色,則返回true. |
| hasAnyRole([role1,role2]) | 如果當前的主體有任何提供的角色(給定的作為一個逗號分隔的字符串列表)的話,返回true |
| hasAuthority([authority]) | 如果當前的主體具有指定的權限,則返回 true. |
| hasAnyAuthority([authority1,authority2]) | 如果當前的主體有任何提供的角色(給定的作為一個逗號分隔的字符串列表)的話,返回true. |
| principal | 允許直接訪問表示當前用戶的主對象 |
| permitAll | 允許所有用戶訪問 |
| denyAll | 不允許用戶訪問 |
| isAnonymous() | 如果當前的主體是一個匿名用戶,則返回true. |
| isRememberMe() | 如果當前的主體是一個匿名用戶,則返回true |
| isAuthenticated() | 如果用戶不是匿名的,則返回 true |
| isFullyAuthenticated() | 如果用戶不是一個匿名的或是一個記住我的用戶返回true |
| hasPermission(Object target, Object permission) | 如果用戶已訪問給定權限的提供的目標,則返回true,例如hasPermission(domainObject, 'read') |
| hasPermission(Object targetId, String targetType, Object permission) | 如果用戶已訪問給定權限的提供的目標,則返回true,例如hasPermission(1, 'com.example.domain.Message', 'read') |
登錄驗證碼
首先得寫一個獲取驗證碼方法,方法能夠生成隨機驗證碼并且把驗證碼存入session供驗證的功能,這里就不寫了
因為Spring Security未提供驗證碼的接口,所以需要我們自己寫一個過濾器處理
VaildCodeFilter 驗證碼校驗過濾器
/*** Security 登錄驗證碼驗證** @author Peng*/ public class VaildCodeFilter extends OncePerRequestFilter {private SecurityLoginFailHandler securityLoginFailHandler;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 登錄路徑必須為/api/formlogin才生效if ("/api/formlogin".equals(request.getRequestURI())) {HttpSession session = request.getSession();try {String code = request.getParameter(頁面驗證碼字段);String sessionCode = (String) session.getAttribute(驗證碼sessionId);if (code == null || sessionCode == null) {throw new VaildCodeException("驗證碼不存在");}if (!sessionCode.equals(code)) {throw new VaildCodeException("驗證碼不匹配");}} catch (VaildCodeException e) {securityLoginFailHandler.onAuthenticationFailure(request, response, e);return;}session.removeAttribute(驗證碼sessionId);}filterChain.doFilter(request, response);}public void setSecurityLoginFailHandler(SecurityLoginFailHandler securityLoginFailHandler) {this.securityLoginFailHandler = securityLoginFailHandler;} } 最后在WebSecurityConfig configure中配置
@Overrideprotected void configure(HttpSecurity http) throws Exception {// 在UsernamePasswordAuthenticationFilter過濾器前添加自定義驗證碼過濾器VaildCodeFilter vaildCodeFilter = new VaildCodeFilter();vaildCodeFilter.setSecurityLoginFailHandler(securityLoginFailHandler);http.addFilterBefore(vaildCodeFilter, UsernamePasswordAuthenticationFilter.class);http.formLogin().loginPage("/api/login").successHandler(securityLoginSuccessHandler).failureHandler(securityLoginFailHandler).loginProcessingUrl("/api/formlogin");http.authorizeRequests().antMatchers("/api/login", "/static/*", "/api/codeImg").permitAll().antMatchers("/api/me").hasRole("ADMIN").anyRequest().authenticated();http.logout().logoutUrl("/logout").logoutSuccessUrl("/api/login").invalidateHttpSession(true).addLogoutHandler(securityLoginoutHandler).logoutSuccessHandler(securityLoginoutSuccessHandler);// 指定退出登錄后需要刪除的cookie名稱,多個cookie之間以逗號分隔。 // .deleteCookies(cookieNamesToClear)http.csrf().disable();}
總結
以上是生活随笔為你收集整理的Spring Security学习(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用memcache缓存
- 下一篇: Spring Boot + Mybati