溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

SpringSecurity中怎么保護(hù)程序安全

發(fā)布時間:2021-07-24 14:49:38 來源:億速云 閱讀:136 作者:Leah 欄目:編程語言

SpringSecurity中怎么保護(hù)程序安全,針對這個問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

首先,引入依賴:

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-security</artifactId></dependency>

引入此依賴之后,你的web程序?qū)碛幸韵鹿δ埽?/p>

所有請求路徑都需要認(rèn)證  不需要特定的角色和權(quán)限  沒有登錄頁面,使用HTTP基本身份認(rèn)證  只有一個用戶,名稱為user

配置SpringSecurity

springsecurity配置項,最好保存在一個單獨的配置類中:

@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {  }

配置用戶認(rèn)證方式

首先,要解決的就是用戶注冊,保存用戶的信息。springsecurity提供四種存儲用戶的方式:

基于內(nèi)存(生產(chǎn)肯定不使用)  基于JDBC  基于LDAP  用戶自定義(最常用)

使用其中任意一種方式,需要覆蓋configure(AuthenticationManagerBuilder auth)方法:

@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {  @Override  protected void configure(AuthenticationManagerBuilder auth) throws Exception {  }}

1.基于內(nèi)存

@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {  auth.inMemoryAuthentication()      .withUser("zhangsan").password("123").authorities("ROLE_USER")      .and()      .withUser("lisi").password("456").authorities("ROLE_USER");}

2.基于JDBC

@AutowiredDataSource dataSource;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {  auth.jdbcAuthentication()    .dataSource(dataSource);}

基于JDBC的方式,你必須有一些特定表表,而且字段滿足其查詢規(guī)則:

public static final String DEF_USERS_BY_USERNAME_QUERY =  "select username,password,enabled " +  "from users " +  "where username = ?";public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =  "select username,authority " +  "from authorities " +  "where username = ?";public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY =   "select g.id, g.group_name, ga.authority " +  "from groups g, group_members gm, group_authorities ga " +  "where gm.username = ? " +  "and g.id = ga.group_id " +  "and g.id = gm.group_id";

當(dāng)然,你可以對這些語句進(jìn)行一下修改:

@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {   auth.jdbcAuthentication().dataSource(dataSource)    .usersByUsernameQuery("select username, password, enabled from Users " +               "where username=?")    .authoritiesByUsernameQuery("select username, authority from UserAuthorities " +                  "where username=?");

這有一個問題,你數(shù)據(jù)庫中的密碼可能是一種加密方式加密過的,而用戶傳遞的是明文,比較的時候需要進(jìn)行加密處理,springsecurity也提供了相應(yīng)的功能:

@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {   auth.jdbcAuthentication().dataSource(dataSource)    .usersByUsernameQuery("select username, password, enabled from Users " +               "where username=?")    .authoritiesByUsernameQuery("select username, authority from UserAuthorities " +                  "where username=?")    .passwordEncoder(new StandardPasswordEncoder("53cr3t");

passwordEncoder方法傳遞的是PasswordEncoder接口的實現(xiàn),其默認(rèn)提供了一些實現(xiàn),如果都不滿足,你可以實現(xiàn)這個接口:

BCryptPasswordEncoder  NoOpPasswordEncoder  Pbkdf2PasswordEncoder  SCryptPasswordEncoder  StandardPasswordEncoder(SHA-256)

3.基于LDAP

@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {  auth.ldapAuthentication()    .userSearchBase("ou=people")    .userSearchFilter("(uid={0})")    .groupSearchBase("ou=groups")    .groupSearchFilter("member={0}")    .passwordCompare()    .passwordEncoder(new BCryptPasswordEncoder())    .passwordAttribute("passcode")    .contextSource()      .root("dc=tacocloud,dc=com")      .ldif("classpath:users.ldif");

4.用戶自定義方式(最常用)

首先,你需要一個用戶實體類,它實現(xiàn)UserDetails接口,實現(xiàn)這個接口的目的是為框架提供更多的信息,你可以把它看作框架使用的實體類:

@Datapublic class User implements UserDetails {  private Long id;  private String username;  private String password;  private String fullname;  private String city;  private String phoneNumber;    @Override  public Collection<? extends GrantedAuthority> getAuthorities() {    return null;  }  @Override  public boolean isAccountNonExpired() {    return false;  }  @Override  public boolean isAccountNonLocked() {    return false;  }  @Override  public boolean isCredentialsNonExpired() {    return false;  }  @Override  public boolean isEnabled() {    return false;  }}

有了實體類,你還需要Service邏輯層,springsecurity提供了UserDetailsService接口,見名知意,你只要通過loadUserByUsername返回一個UserDetails對象就成,無論是基于文件、基于數(shù)據(jù)庫、還是基于LDAP,剩下的對比判斷交個框架完成:

@Servicepublic class UserService implements UserDetailsService {    @Override  public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {    return null;  }  }

最后,進(jìn)行應(yīng)用:

@Autowiredprivate UserDetailsService userDetailsService;@Beanpublic PasswordEncoder encoder() {  return new StandardPasswordEncoder("53cr3t");}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {  auth.userDetailsService(userDetailsService)    .passwordEncoder(encoder());}

配置認(rèn)證路徑

知道了如何認(rèn)證,但現(xiàn)在有幾個問題,比如,用戶登錄頁面就不需要認(rèn)證,可以用configure(HttpSecurity http)對認(rèn)證路徑進(jìn)行配置:

@Overrideprotected void configure(HttpSecurity http) throws Exception {   }

你可以通過這個方法,實現(xiàn)以下功能:

在提供接口服務(wù)前,判斷請求必須滿足某些條件  配置登錄頁面  允許用戶注銷登錄  跨站點偽造請求防護(hù)

1.保護(hù)請求

@Overrideprotected void configure(HttpSecurity http) throws Exception {   http.authorizeRequests()    .antMatchers("/design", "/orders").hasRole("ROLE_USER")    .antMatchers(“/”, "/**").permitAll();}

要注意其順序,除了hasRole和permitAll還有其它訪問認(rèn)證方法:

access(String)      如果給定的SpEL表達(dá)式的計算結(jié)果為true,則允許訪問              anonymous()      允許訪問匿名用戶              authenticated()      允許訪問經(jīng)過身份驗證的用戶              denyAll()      無條件拒絕訪問              fullyAuthenticated()      如果用戶完全通過身份驗證,則允許訪問              hasAnyAuthority(String...)      如果用戶具有任何給定權(quán)限,則允許訪問              hasAnyRole(String...)      如果用戶具有任何給定角色,則允許訪問              hasAuthority(String)      如果用戶具有給定權(quán)限,則允許訪問              hasIpAddress(String)      如果請求來自給定的IP地址,則允許訪問              hasRole(String)      如果用戶具有給定角色,則允許訪問              not()      否定任何其他訪問方法的影響              permitAll()      允許無條件訪問              rememberMe()      允許通過remember-me進(jìn)行身份驗證的用戶訪問

大部分方法是為特定方式準(zhǔn)備的,但是access(String)可以使用SpEL進(jìn)一些特殊的設(shè)置,但其中很大一部分也和上面的方法相同:

authentication      用戶的身份驗證對象              denyAll      始終評估為false              hasAnyRole(list of roles)      如果用戶具有任何給定角色,則為true              hasRole(role)      如果用戶具有給定角色,則為true              hasIpAddress(IP address)      如果請求來自給定的IP地址,則為true              isAnonymous()      如果用戶是匿名用戶,則為true              isAuthenticated()      如果用戶已通過身份驗證,則為true              isFullyAuthenticated()      如果用戶已完全通過身份驗證,則為true(未通過remember-me進(jìn)行身份驗證)              isRememberMe()      如果用戶通過remember-me進(jìn)行身份驗證,則為true              permitAll      始終評估為true              principal      用戶的主要對象

示例:

@Overrideprotected void configure(HttpSecurity http) throws Exception {   http.authorizeRequests()    .antMatchers("/design", "/orders").access("hasRole('ROLE_USER')")    .antMatchers(“/”, "/**").access("permitAll");}

方法作用
表達(dá)式作用

@Overrideprotected void configure(HttpSecurity http) throws Exception {  http.authorizeRequests()    .antMatchers("/design", "/orders").access("hasRole('ROLE_USER') && " +     "T(java.util.Calendar).getInstance().get("+"T(java.util.Calendar).DAY_OF_WEEK) == " +     "T(java.util.Calendar).TUESDAY")    .antMatchers(“/”, "/**").access("permitAll");}

2.配置登錄頁面

@Overrideprotected void configure(HttpSecurity http) throws Exception {   http.authorizeRequests()    .antMatchers("/design", "/orders").access("hasRole('ROLE_USER')")    .antMatchers(“/”, "/**").access("permitAll")    .and()    .formLogin()      .loginPage("/login");}// 增加視圖處理器@Overridepublic void addViewControllers(ViewControllerRegistry registry) {  registry.addViewController("/").setViewName("home");   registry.addViewController("/login");}

默認(rèn)情況下,希望傳遞的是username和password,當(dāng)然你可以修改:

.and()  .formLogin()    .loginPage("/login")    .loginProcessingUrl("/authenticate")    .usernameParameter("user")    .passwordParameter("pwd")

也可修改默認(rèn)登錄成功的頁面:

.and()  .formLogin()    .loginPage("/login")    .defaultSuccessUrl("/design")

3.配置登出

.and()  .logout()     .logoutSuccessUrl("/")

4.csrf攻擊

springsecurity默認(rèn)開啟了防止csrf攻擊,你只需要在傳遞的時候加上:

<input type="hidden" name="_csrf" th:value="${_csrf.token}"/>

當(dāng)然,你也可以關(guān)閉,但是不建議這樣做:

.and()  .csrf()    .disable()

知道用戶是誰

僅僅控制用戶登錄有時候是不夠的,你可能還想在程序的其它地方獲取已經(jīng)登錄的用戶信息,有幾種方式可以做到:

將Principal對象注入控制器方法  將Authentication對象注入控制器方法  使用SecurityContextHolder獲取安全上下文  使用@AuthenticationPrincipal注解方法

1.將Principal對象注入控制器方法

@PostMappingpublic String processOrder(@Valid Order order, Errors errors,SessionStatus sessionStatus,Principal principal) {  ...   User user = userRepository.findByUsername(principal.getName());  order.setUser(user);  ...}

2.將Authentication對象注入控制器方法

@PostMappingpublic String processOrder(@Valid Order order, Errors errors, SessionStatus sessionStatus, Authentication authentication) {  ...  User user = (User) authentication.getPrincipal();  order.setUser(user);  ...}

3.使用SecurityContextHolder獲取安全上下文

Authentication authentication =  SecurityContextHolder.getContext().getAuthentication();  User user = (User) authentication.getPrincipal();

4.使用@AuthenticationPrincipal注解方法

@PostMappingpublic String processOrder(@Valid Order order, Errors errors,SessionStatus sessionStatus, @AuthenticationPrincipal User user) {  if (errors.hasErrors()) {    return "orderForm";   }   order.setUser(user);  orderRepo.save(order);  sessionStatus.setComplete();  return "redirect:/";}

關(guān)于SpringSecurity中怎么保護(hù)程序安全問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI