欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot?Security從入門到實(shí)戰(zhàn)示例教程

 更新時(shí)間:2022年05月06日 09:52:32   作者:lakernote  
Spring?Security是一個(gè)功能強(qiáng)大且高度可定制的身份驗(yàn)證和訪問控制框架,接下來通過本文給大家介紹SpringBoot?Security從入門到實(shí)戰(zhàn)示例教程,感興趣的朋友一起看看吧

前言

Spring Security是一個(gè)功能強(qiáng)大且高度可定制的身份驗(yàn)證和訪問控制框架。提供了完善的認(rèn)證機(jī)制和方法級(jí)的授權(quán)功能。是一款非常優(yōu)秀的權(quán)限管理框架。它的核心是一組過濾器鏈,不同的功能經(jīng)由不同的過濾器。這篇文章給大家講解SpringBoot Security從入門到實(shí)戰(zhàn),內(nèi)容如下所示:

入門

測(cè)試接口

假設(shè)我們用下面的接口做權(quán)限測(cè)試。

@RestController
public class LakerController {
    @GetMapping("/laker")
    public String laker() {
        return IdUtil.simpleUUID();
    }
    @GetMapping("/laker/q")
    public String lakerQ() {
        return IdUtil.simpleUUID();
    }
}

瀏覽器訪問:http://localhost:8080/laker,結(jié)果如下:

增加依賴

pom.xml,添加 spring-boot-starter-securtiy 依賴。

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

再次訪問http://localhost:8080/laker,結(jié)果如下:

簡要解析

我們?cè)L問http://localhost:8080/laker,security判斷我們沒有登錄,則會(huì)302重定向http://localhost:8080/login(默認(rèn))

  • security會(huì)返回一個(gè)默認(rèn)的登錄頁。
  • 默認(rèn)用戶名為:user,密碼在服務(wù)啟動(dòng)時(shí),會(huì)隨機(jī)生成一個(gè),可以查看啟動(dòng)日志如下:

2022-05-02 21:01:03.697  INFO 17896 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2022-05-02 21:01:03.825  INFO 17896 --- [           main] .s.s.UserDetailsServiceAutoConfiguration : 

Using generated security password: e53fef6a-3f61-43c3-9609-ce88fd7c0841

當(dāng)然,可以通過配置文件設(shè)置默認(rèn)的用戶名、密碼、角色。

spring:
  security:
    user:
      # 默認(rèn)是 user
      name: laker
      password: laker
      roles:
        - ADMIN
        - TESTER

以上的默認(rèn)都是可以修改的哈。

判斷是否登錄使用傳統(tǒng)的cookie session模式哈,JSESSIONID E3512CD1A81DB7F2144C577BA38D2D92 HttpOnly true

自定義配置

實(shí)際項(xiàng)目中我們的用戶、密碼、角色、權(quán)限、資源都是存儲(chǔ)在數(shù)據(jù)庫中的,我們可以通過自定義類繼承 WebSecurityConfigurerAdapter,從而實(shí)現(xiàn)對(duì) Spring Security 更多的自定義配置。

@Configuration
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
		...
}

配置密碼加密方式

必須配置,否則報(bào)空指針異常。

   @Bean
    PasswordEncoder passwordEncoder() {
        // 不加密
        return NoOpPasswordEncoder.getInstance();
    }

Spring Security 提供了多種密碼加密方案,官方推薦使用 BCryptPasswordEncoder

BCryptPasswordEncoder 使用 BCrypt 強(qiáng)哈希函數(shù),開發(fā)者在使用時(shí)可以選擇提供 strengthSecureRandom 實(shí)例。strength 取值在 4~31 之間(默認(rèn)為 10)。strength 越大,密鑰的迭代次數(shù)越多(密鑰迭代次數(shù)為 2^strength)。如果是數(shù)據(jù)庫認(rèn)證,庫里的密碼同樣也存放加密后的密碼。同一密碼每次 Bcrypt 生成的結(jié)果都會(huì)變化不會(huì)重復(fù)。

配置AuthenticationManagerBuilder 認(rèn)證用戶、角色權(quán)限

支持直接配置內(nèi)存認(rèn)證模式和配置UserDetailsServiceBean方式

內(nèi)存認(rèn)證模式,實(shí)際項(xiàng)目不用這個(gè)哦。(僅做了解)

 /**
     * 配置用戶及其對(duì)應(yīng)的角色
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("admin").password("123").roles("ADMIN", "USER")
                .and()
                .withUser("laker").password("123").roles("USER");
    }

上面在UserDetailsService的實(shí)現(xiàn)之一InMemoryUserDetailsManager中,即存儲(chǔ)在內(nèi)存中。

自定義UserDetailsServiceBean方式,實(shí)際項(xiàng)目都是使用這個(gè),可定制化程度高。

步驟一:定義一個(gè)LakerUserService實(shí)現(xiàn)UserDetailsService,配置成SpringBean。該方法將在用戶登錄時(shí)自動(dòng)調(diào)用。

@Service
public class LakerUserService implements UserDetailsService {
    @Autowired
    UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // username 就是前端傳遞的例如 laker 123,即 laker
        User user = userMapper.loadUserByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("賬戶不存在!");
        }
        user.setAuthorities(...);
        return user;
    }
}

username

password(加密后的密碼)

authorities

返回的Bean中以上3個(gè)都不能為空。返回User后由系統(tǒng)提供的 DaoAuthenticationProvider 類去比對(duì)密碼是否正確。

Spring Security默認(rèn)支持表單請(qǐng)求登錄的源碼,UsernamePasswordAuthenticationFilter.java

步驟二:在把自定義的LakerUserService裝載進(jìn)去.

@Autowired
UserService userService;
...
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userService);
}

步驟三:其中我們的業(yè)務(wù)用戶User必須要實(shí)現(xiàn)UserDetails接口,并實(shí)現(xiàn)該接口中的 7 個(gè)方法:

  • getAuthorities():獲取當(dāng)前用戶對(duì)象所具有的權(quán)限信息
  • getPassword():獲取當(dāng)前用戶對(duì)象的密碼

返回的密碼和用戶輸入的登錄密碼不匹配,會(huì)自動(dòng)拋出 BadCredentialsException 異常。

  • getUsername():獲取當(dāng)前用戶對(duì)象的用戶名
  • isAccountNonExpired():當(dāng)前賬戶是否未過期
  • isAccountNonLocked():當(dāng)前賬戶是否未鎖定
  • 返回了 false,會(huì)自動(dòng)拋出 AccountExpiredException 異常。
  • isCredentialsNonExpired():當(dāng)前賬戶密碼是否未過期
  • isEnabled():當(dāng)前賬戶是否可用
@Data
public class User implements UserDetails {
    private Integer id;
    private String username;
    private String password;
    private Boolean enabled;
    private Boolean locked;
    private List<String> authorities;
    @Override
    public String getPassword() {
        return password;
    }
    @Override
    public String getUsername() {
        return username;
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return !locked;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return enabled;
    }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authoritiesList = new ArrayList<>();
        for (String authority : authorities) {
            authoritiesList.add(new SimpleGrantedAuthority(authority));
        }
        return authoritiesList;
    }
}

配置HttpSecurity Url訪問權(quán)限

  /**
     * 配置 URL 訪問權(quán)限
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //
        http    // 1.開啟 HttpSecurity 配置
                .authorizeRequests()
                // laker/** 模式URL必須具備laker.query
                .antMatchers("/laker/**").hasAnyAuthority("laker.query")
                // 用戶訪問其它URL都必須認(rèn)證后訪問(登錄后訪問)
                .anyRequest().authenticated()
                .and()
                // 2.開啟表單登錄,前后端分離的時(shí)候不用這個(gè)
                .formLogin()
                // 未登錄時(shí) 重定向的url 默認(rèn)是/login 內(nèi)置的頁面,可以自己自定義哈。一般前后端分離,不用這個(gè)
//                .loginPage("/login")
                //
//                .defaultSuccessUrl("/user",true)
//                .usernameParameter("username") // default is username
//                .passwordParameter("password") // default is password
//                .loginPage("/authentication/login") // default is /login with an HTTP get
//                .failureUrl("/authentication/login?failed") // default is /login?error
//                .loginProcessingUrl("/authentication/login/process") // default is /login
                .and()
                // 3.關(guān)閉csrf,前后端分離不需要這個(gè)。
                .csrf().disable();
                //授權(quán)碼模式需要 會(huì)彈出默認(rèn)自帶的登錄框
                http.httpBasic();   
        		// 開啟注銷登錄的配置 
                http.logout()
                    // 配置注銷登錄請(qǐng)求URL為"/logout"(默認(rèn)也就是 /logout)
                    .logoutSuccessUrl("/logout")
                    .clearAuthentication(true) // 清除身份認(rèn)證信息
                    .invalidateHttpSession(true) // 使 session 失效;
    }
  • formLogin() 表示開啟表單登錄
  • **defaultSuccessUrl()**表示默認(rèn)登錄驗(yàn)證成功跳轉(zhuǎn)的url,默認(rèn)重定向到上次訪問未成功的,如果沒有則重定向到/.
  • loginProcessingUrl() 方法配置登錄接口為“/login”,即可以直接調(diào)用“/login”接口,發(fā)起一個(gè) POST 請(qǐng)求進(jìn)行登錄,登錄參數(shù)中用戶名必須為 username,密碼必須為 password,配置 loginProcessingUrl 接口主要是方便 Ajax 或者移動(dòng)端調(diào)用登錄接口。

    anyRequest          |   匹配所有請(qǐng)求路徑
    access              |   SpringEl表達(dá)式結(jié)果為true時(shí)可以訪問
    anonymous           |   匿名可以訪問 所有人都能訪問,但是帶上 token訪問后會(huì)報(bào)錯(cuò)403
    denyAll             |   用戶不能訪問 所有人都能訪問,包括帶上 token 訪問
    fullyAuthenticated  |   用戶完全認(rèn)證可以訪問(非remember-me下自動(dòng)登錄)
    hasAnyAuthority     |   如果有參數(shù),參數(shù)表示權(quán)限,則其中任何一個(gè)權(quán)限可以訪問
    hasAnyRole          |   如果有參數(shù),參數(shù)表示角色,則其中任何一個(gè)角色可以訪問
    hasAuthority        |   如果有參數(shù),參數(shù)表示權(quán)限,則其權(quán)限可以訪問
    hasIpAddress        |   如果有參數(shù),參數(shù)表示IP地址,如果用戶IP和參數(shù)匹配,則可以訪問
    hasRole             |   如果有參數(shù),參數(shù)表示角色,則其角色可以訪問
    permitAll           |   用戶可以任意訪問
    rememberMe          |   允許通過remember-me登錄的用戶訪問
    authenticated       |   用戶登錄后可訪問

自定義successHandler

登錄成功后默認(rèn)是重定向url,我們可以自定義返回json用于前后端分離場(chǎng)景以及其他邏輯,例如成功之后發(fā)短信等。

http.formLogin().successHandler((req, resp, authentication) -> {
    // 發(fā)短信哈
    Object principal = authentication.getPrincipal();
    resp.setContentType("application/json;charset=utf-8");
    PrintWriter out = resp.getWriter();
    out.write(new ObjectMapper().writeValueAsString(principal));
    out.flush();
    out.close();
})

自定義failureHandler

登錄失敗回調(diào)

http.formLogin().failureHandler((req, resp, e) -> {
    resp.setContentType("application/json;charset=utf-8");
    PrintWriter out = resp.getWriter();
    out.write(e.getMessage());
    out.flush();
    out.close();
})

自定義未認(rèn)證處理

http.exceptionHandling()
.authenticationEntryPoint((req, resp, authException) -> {
            resp.setContentType("application/json;charset=utf-8");
            PrintWriter out = resp.getWriter();
            out.write("尚未登錄,請(qǐng)先登錄");
            out.flush();
            out.close();
        }
);

自定義權(quán)限不足處理

http.exceptionHandling()
				//沒有權(quán)限,返回json
				.accessDeniedHandler((request,response,ex) -> {
					response.setContentType("application/json;charset=utf-8");
					response.setStatus(HttpServletResponse.SC_FORBIDDEN);
					PrintWriter out = response.getWriter();
					Map<String,Object> map = new HashMap<String,Object>();
					map.put("code",403);
					map.put("message", "權(quán)限不足");
					out.write(objectMapper.writeValueAsString(map));
					out.flush();
					out.close();
				})

自定義注銷登錄

.logout()
.logoutUrl("/logout")
.logoutSuccessHandler((req, resp, authentication) -> {
    resp.setContentType("application/json;charset=utf-8");
    PrintWriter out = resp.getWriter();
    out.write("注銷成功");
    out.flush();
    out.close();
})

前后端分離場(chǎng)景

上面的都是入門的,實(shí)際項(xiàng)目中一般都是前后端分離的,在登錄時(shí)都是自定義登錄接口,例如登錄接口是restful風(fēng)格,增加了其他的驗(yàn)證碼參數(shù),還使用jwt來完成登錄鑒權(quán)等。

提供登錄接口

該接口需要在配置當(dāng)中放行,未授權(quán)訪問需要授權(quán)的請(qǐng)求時(shí),會(huì)返回401或者403狀態(tài)碼,前端可以根據(jù)這個(gè)進(jìn)行路由提示處理。

@RestController
public class LoginController {
   @Autowired
   LoginService ...
   @PostMapping("/login")
   public  login(@RequestBody Login login){
       ...
       return token;
   }
}

Service層創(chuàng)建UsernamePasswordAuthenticationToken對(duì)象,把用戶名和密碼封裝成Authentication對(duì)象.

@Service
public class LoginServiceImpl implements LoginService {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    public  doLogin(Login login) {
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password;
        Authentication  authenticate
        try {         // 該方法會(huì)去調(diào)用UserDetailsServiceImpl.loadUserByUsername
             authenticate = authenticationManager.authenticate(authenticationToken);
        } catch (AuthenticationException e) {
            e.printStackTrace();
        }
 
        if (Objects.isNull(authenticate)) {
            //用戶名密碼錯(cuò)誤
            throw new ServicesException(...);
        }
        User authUser = (User) authenticate.getPrincipal();
        String token = JwtUtil.createJWT(username);
        Map<String, String> map = new HashMap<>();
        map.put("token", token);
        return map;
    }
}

自定義認(rèn)證過濾器

坊間有2種實(shí)現(xiàn)方式。

方式一:繼承UsernamePasswordAuthenticationFilter的寫法需要使用登陸成功處理器、失敗處理器等,還是需要按照security這一套來玩。

Spring Security默認(rèn)支持表單請(qǐng)求登錄的源碼UsernamePasswordAuthenticationFilter.java

方式二:使用Filter的寫法沒有任何限制怎么玩都行,比如說添加其他參數(shù)驗(yàn)證碼,返回json,token鑒權(quán)等。

@Component
public class LakerOncePerRequestFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {
        String token = request.getHeader("Authorization");
        if (!StringUtils.isEmpty(token) )
        {
            // 校驗(yàn)token ...
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user, null, authorities;
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));                                     SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }
        chain.doFilter(request, response);
    }
}                                                                                                           //3、在UsernamePasswordAuthenticationFilter前添加認(rèn)證過濾器
http.addFilterBefore(lakerOncePerRequestFilter, UsernamePasswordAuthenticationFilter.class);

鑒權(quán)

1.注解鑒權(quán)

  • SpringSecurity配置類中開啟方法級(jí)的認(rèn)證
  • 使用 @PreAuthorize注解在方法或者類
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
...
@RestController
public class Controller {
    @GetMapping("/hello")
    @PreAuthorize("hasAnyAuthority('laker.query')")
    public String test() {
}

2.自定義Bean動(dòng)態(tài)鑒權(quán)

因?yàn)?code>@PreAuthorize支持SpringEL表達(dá)式,所以可以支持自定義SpringBean動(dòng)態(tài)鑒權(quán)。

  • 先自定義一個(gè)SpringBean。
  • 使用 @PreAuthorize注解在方法或者類配合@PreAuthorize(“@rbacService.hasPermission(‘xx’)”)
@Component("rbacService")
public class LakerRBACService {
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        Object principal = authentication.getPrincipal();
        if (principal instanceof UserDetails) {
            UserDetails userDetails=(UserDetails)principal;

            /**
             * 該方法主要對(duì)比認(rèn)證過的用戶是否具有請(qǐng)求URL的權(quán)限,有則返回true
             */
            //本次要訪問的資源
              SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(request.getMethod() + "" + request.getRequestURI());

            //用戶擁有的權(quán)限中是否包含請(qǐng)求的url
            return userDetails.getAuthorities().contains(simpleGrantedAuthority);
        }
        return false;
    }
        public boolean hasPermission() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Object principal = authentication.getPrincipal();
        if (principal instanceof UserDetails) {
            UserDetails userDetails = (UserDetails) principal;
            /**
             * 該方法主要對(duì)比認(rèn)證過的用戶是否具有請(qǐng)求URL的權(quán)限,有則返回true
             */
            //本次要訪問的資源
            HttpServletRequest request =((ServletRequestAttributes)
                    RequestContextHolder.getRequestAttributes()).getRequest();

            SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(request.getRequestURI());
            //用戶擁有的權(quán)限中是否包含請(qǐng)求的url
            return userDetails.getAuthorities().contains(simpleGrantedAuthority);
        }
        return false;
    }
}
// controller方法
@PreAuthorize("@rbacService.hasPermission()")
public String test() {
}
// 或者高級(jí)的全局url鑒權(quán)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
         ...
      http.authorizeRequests() //設(shè)置授權(quán)請(qǐng)求,任何請(qǐng)求都要經(jīng)過下面的權(quán)限表達(dá)式處理
          .anyRequest().access("@rbacService.hasPermission(request,authentication)") //權(quán)限表達(dá)式     

3.擴(kuò)展默認(rèn)方法自定義擴(kuò)展根對(duì)象SecurityExpressionRoot

http://www.dbjr.com.cn/article/245172.htm

1.創(chuàng)建自定義根對(duì)象

public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
    public CustomMethodSecurityExpressionRoot(Authentication authentication) {
        super(authentication);
    }
    /**
     * 自定義表達(dá)式
     * @param username 具有權(quán)限的用戶賬號(hào)
     */
    public boolean hasUser(String... username) {
        String name = this.getAuthentication().getName();
        HttpServletRequest request = ((ServletRequestAttributes)
                RequestContextHolder.getRequestAttributes()).getRequest();
        String[] names = username;
        for (String nameStr : names) {
            if (name.equals(nameStr)) {
                return true;
            }
        }
        return false;
    }
}

2.創(chuàng)建自定義處理器

創(chuàng)建自定義處理器,主要是重寫創(chuàng)建根對(duì)象的方法。

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
    @Override
    protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
        CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
        root.setThis(invocation.getThis());
        root.setPermissionEvaluator(getPermissionEvaluator());
        root.setTrustResolver(getTrustResolver());
        root.setRoleHierarchy(getRoleHierarchy());
        root.setDefaultRolePrefix(getDefaultRolePrefix());
        return root;
    }
}

3.配置GlobalMethodSecurityConfiguration
之前我們使用@EnableGlobalMethodSecurity開啟全局方法安全,而這些全局方法級(jí)別的安全配置就在GlobalMethodSecurityConfiguration配置類中。

可以擴(kuò)展這個(gè)類來自定義默認(rèn)值,但必須確保在類上指定@EnableGlobalMethodSecurity 注解,否則會(huì)bean沖突報(bào)錯(cuò)。

@Configuration
// 將EnableGlobalMethodSecurity注解移到這里
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return new CustomMethodSecurityExpressionHandler();
    }
}

4.controller使用自定義方法

@PreAuthorize("hasUser('laker','admin')")
public String test() {
    ...
}

登出

http.logout().logoutUrl("/logout").logoutSuccessHandler((request, response, authentication) -> {
            // 刪除用戶token
    		...
            // 返回json
            response.setContentType("application/json;charset=utf-8");
            response.setStatus(HttpServletResponse.SC_OK);
            PrintWriter out = response.getWriter();
            out.write("OK");
            out.flush();
            out.close();
        });

跨域

@Override
protected void configure(HttpSecurity http) throws Exception {
	http.cors();//允許跨域,配置后SpringSecurity會(huì)自動(dòng)尋找name=corsConfigurationSource的Bean
	http.csrf().disable();//關(guān)閉CSRF防御
}

@Configuration
public class CrosConfig {
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration cores=new CorsConfiguration();
        cores.setAllowCredentials(true);//允許客戶端攜帶認(rèn)證信息
        //springBoot 2.4.1版本之后,不可以用 * 號(hào)設(shè)置允許的Origin,如果不降低版本,則在跨域設(shè)置時(shí)使用setAllowedOriginPatterns方法
       // cores.setAllowedOrigins(Collections.singletonList("*"));//允許所有域名可以跨域訪問
        cores.setAllowedOriginPatterns(Collections.singletonList("*"));
        cores.setAllowedMethods(Arrays.asList("GET","POST","DELETE","PUT","UPDATE"));//允許哪些請(qǐng)求方式可以訪問
        cores.setAllowedHeaders(Collections.singletonList("*"));//允許服務(wù)端訪問的客戶端請(qǐng)求頭
        // 暴露哪些頭部信息(因?yàn)榭缬蛟L問默認(rèn)不能獲取全部頭部信息)
        cores.addExposedHeader(jsonWebTokenUtil.getHeader());
        // 注冊(cè)跨域配置
        // 也可以使用CorsConfiguration 類的 applyPermitDefaultValues()方法使用默認(rèn)配置
        source.registerCorsConfiguration("/**",cores.applyPermitDefaultValues());
        return source;
    }
}

全局配置

@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    UserService userService;

    @Autowired
    TokenFilter tokenFilter;

    /**
     * 配置 URL 訪問權(quán)限
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //
        http    // 1.過濾請(qǐng)求
                .authorizeRequests()
                // 2.對(duì)于登錄login 驗(yàn)證碼captcha 允許訪問
                .antMatchers("/login").permitAll()
                // 用戶訪問其它URL都必須認(rèn)證后訪問(登錄后訪問)
                .anyRequest().authenticated()
                .and()
                // 3.關(guān)閉csrf
                .csrf().disable()
                // 4.基于token,所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                // 5.頁面能不能以 frame、 iframe、 object 形式嵌套在其他站點(diǎn)中,用來避免點(diǎn)擊劫持(clickjacking)攻擊
                .and().headers().frameOptions().disable();
        // 異常處理
        http.exceptionHandling()
                // 未認(rèn)證返回401
                .authenticationEntryPoint((req, response, authException) -> {
                    response.setContentType("application/json;charset=utf-8");
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    PrintWriter out = response.getWriter();
                    out.write("尚未登錄,請(qǐng)先登錄");
                    out.flush();
                    out.close();
                })
                // 沒有權(quán)限,返回403 json
                .accessDeniedHandler((request, response, ex) -> {
                    response.setContentType("application/json;charset=utf-8");
                    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                    PrintWriter out = response.getWriter();
                    Map<String, Object> map = new HashMap<String, Object>();
                    map.put("code", 403);
                    map.put("message", "權(quán)限不足");
                    out.write(JSONUtil.toJsonPrettyStr(map));
                    out.flush();
                    out.close();
                });
        // 配置登出
        http.logout().logoutUrl("/logout").logoutSuccessHandler((request, response, authentication) -> {
                // 刪除用戶token
            response.setContentType("application/json;charset=utf-8");
            response.setStatus(HttpServletResponse.SC_OK);
            PrintWriter out = response.getWriter();
            out.write("OK");
            out.flush();
            out.close();
        });
        // 添加JWT filter
        http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
    }

    /**
     * 配置用戶及其對(duì)應(yīng)的角色
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }
    /**
     * 適用于靜態(tài)資源的防攔截,css、js、image 等文件
     * 配置的url不會(huì)保護(hù)它們免受CSRF、XSS、Clickjacking等的影響。
     * 相反,如果您想保護(hù)端點(diǎn)免受常見漏洞的侵害,請(qǐng)參閱configure(HttpSecurity)
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/css/**", "/js/**");
    }
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    /**
     * 解決 無法直接注入 AuthenticationManager
     *
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception
    {
        return super.authenticationManagerBean();
    }
}

參考:

https://blog.csdn.net/X_lsod/article/details/122914659

https://blog.csdn.net/godleaf/article/details/108318403

https://blog.csdn.net/qq_43437874/article/details/119543579

到此這篇關(guān)于SpringBoot Security從入門到實(shí)戰(zhàn)示例教程的文章就介紹到這了,更多相關(guān)SpringBoot Security入門內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java 抽象類與接口的區(qū)別介紹

    java 抽象類與接口的區(qū)別介紹

    這篇文章主要介紹了java 抽象類與接口的區(qū)別介紹的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • Java超詳細(xì)精講數(shù)據(jù)結(jié)構(gòu)之bfs與雙端隊(duì)列

    Java超詳細(xì)精講數(shù)據(jù)結(jié)構(gòu)之bfs與雙端隊(duì)列

    廣搜BFS的基本思想是: 首先訪問初始點(diǎn)v并將其標(biāo)志為已經(jīng)訪問。接著通過鄰接關(guān)系將鄰接點(diǎn)入隊(duì)。然后每訪問過一個(gè)頂點(diǎn)則出隊(duì)。按照順序,訪問每一個(gè)頂點(diǎn)的所有未被訪問過的頂點(diǎn)直到所有的頂點(diǎn)均被訪問過。廣度優(yōu)先遍歷類似與層次遍歷
    2022-07-07
  • java獲取包下被指定注解的類過程解析

    java獲取包下被指定注解的類過程解析

    這篇文章主要介紹了java獲取包下被指定注解的類過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • Java的JSON轉(zhuǎn)換類庫GSON的基礎(chǔ)使用教程

    Java的JSON轉(zhuǎn)換類庫GSON的基礎(chǔ)使用教程

    GSON是谷歌開源的一款Java對(duì)象與JSON對(duì)象互相轉(zhuǎn)換的類庫,Java的JSON轉(zhuǎn)換類庫GSON的基礎(chǔ)使用教程,需要的朋友可以參考下
    2016-06-06
  • Java?設(shè)計(jì)模式以虹貓藍(lán)兔的故事講解單例模式

    Java?設(shè)計(jì)模式以虹貓藍(lán)兔的故事講解單例模式

    單例模式(Singleton?Pattern)是?Java?中最簡單的設(shè)計(jì)模式之一。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式
    2022-03-03
  • Java實(shí)現(xiàn)連連看算法

    Java實(shí)現(xiàn)連連看算法

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)連連看算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • Java 使用JdbcTemplate 中的queryForList發(fā)生錯(cuò)誤解決辦法

    Java 使用JdbcTemplate 中的queryForList發(fā)生錯(cuò)誤解決辦法

    這篇文章主要介紹了Java 使用JdbcTemplate 中的queryForList發(fā)生錯(cuò)誤解決辦法的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • Spring?@DateTimeFormat日期格式化時(shí)注解場(chǎng)景分析

    Spring?@DateTimeFormat日期格式化時(shí)注解場(chǎng)景分析

    這篇文章主要介紹了Spring?@DateTimeFormat日期格式化時(shí)注解場(chǎng)景分析,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-05-05
  • Spring中緩存注解@Cache的使用詳解

    Spring中緩存注解@Cache的使用詳解

    這篇文章主要介紹了Spring中緩存注解@Cache的使用詳解,使用注解對(duì)數(shù)據(jù)進(jìn)行緩存功能的框架,只需要簡單地加一個(gè)注解,就能實(shí)現(xiàn)緩存功能,大大簡化我們?cè)跇I(yè)務(wù)中操作緩存的代碼,需要的朋友可以參考下
    2023-07-07
  • Java通過工廠、Map容器創(chuàng)建對(duì)象的方法

    Java通過工廠、Map容器創(chuàng)建對(duì)象的方法

    這篇文章主要介紹了Java通過工廠、Map容器創(chuàng)建對(duì)象的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03

最新評(píng)論