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

springsecurity實現(xiàn)用戶登錄認(rèn)證快速使用示例代碼(前后端分離項目)

 更新時間:2024年03月21日 09:39:47   作者:兩年半的個人練習(xí)生^_^  
這篇文章主要介紹了springsecurity實現(xiàn)用戶登錄認(rèn)證快速使用示例代碼(前后端分離項目),本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧

ps:該文章適合未系統(tǒng)學(xué)習(xí)springsecurity快速使用,可以直接cv使用,只有部分源碼講解,個人覺得先會用了再深究原理

1、引入依賴

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

引入springsecurity依賴后,該依賴會自動生成默認(rèn)登陸頁面和登錄名(user)和密碼(控制臺)

我們使用這個用戶名和密碼登陸后才可以對資源進(jìn)行訪問

因為項目是前后端分離項目,因此并不需要它默認(rèn)生成的登錄頁面和默認(rèn)用戶名密碼嗎,需要查詢數(shù)據(jù)庫進(jìn)行登錄

2、創(chuàng)建類繼承WebSecurityConfigurerAdapter

我們創(chuàng)建一個配置類SecurityConfig(使用@Configuration標(biāo)記)并且繼承WebSecurityConfigurerAdapter類,重寫里面的幾個配置方法即可對springsecurity進(jìn)行配置

(1)重寫里面的configure(HttpSecurity http)方法

跟進(jìn)configure(HttpSecurity http)查看源碼中的方法做了什么

我們可以看到源碼中的該方法中默認(rèn)對所有的請求進(jìn)行攔截,并且默認(rèn)生成表單登錄頁面,并且使用基本認(rèn)證。

我們只需要重寫該方法就可以自己進(jìn)行配置了

.csrf().disable()
.cors()        csrf建議關(guān)閉,cors前后端分離項目建議打開
.mvcMatchers("/admin/login").anonymous()   對這個接口可以匿名訪問,也就是不需要認(rèn)證
.mvcMatchers("/admin/save").permitAll()    對這個接口也不做認(rèn)證
.mvcMatchers("/user/save").authenticated()  對這個接口需要認(rèn)證才能訪問

這個.mvcMatchers的參數(shù)也可以是數(shù)組形式

.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)

添加過濾器,這里的tokenAuthenticationFilter是我自己定義的,這個意思將自定義的這個過濾器放在UsernamePasswordAuthenticationFilter.class這個過濾器之前,這個過濾器是springsecurity提供的認(rèn)證過濾器,像我們的這個tokenAuthenticationFilter是需要在認(rèn)證之前進(jìn)行的

.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint);

這個是添加了一個認(rèn)證異常處理器authenticationEntryPoint,authenticationEntryPoint也是我們自定義的,就是當(dāng)用戶未經(jīng)過認(rèn)證時返回的結(jié)果,通常當(dāng)未登錄訪問接口時返回給前端的異常信息就在這里定義

這樣我們就大致完成了這個方法中登錄認(rèn)證功能的一些配置

(2)重寫AuthenticationManager authenticationManagerBean()

我們需要使用他里面的方法進(jìn)行登錄認(rèn)證,并使用@Bean標(biāo)注到spring容器中

@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
}

(3)密碼加密工具

@Bean
public PasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();
}

使用這個進(jìn)行密碼加密,springsecurity提供了很多密碼加密方法,用這個就可以,可以點進(jìn)去查看PasswordEncoder這個方法,這是個接口,實現(xiàn)了很多加密方法

然后這樣我們就大致完成了這個類的配置

如果有swagger等靜態(tài)資源配置,可以重寫這個方法

/**
 * 配置哪些請求不攔截
 * 排除swagger相關(guān)請求
 * @param web
 * @throws Exception
 */
@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/favicon.ico","/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**", "/doc.html");
}
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private TokenAuthenticationFilter tokenAuthenticationFilter;
    @Autowired
    private AuthenticationEntryPointImpl authenticationEntryPoint;
   /* @Autowired
    private UserDetailsService userDetailsService;*/
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .cors()
                .and()
                .authorizeRequests()
                .mvcMatchers("/admin/login").anonymous()
                .mvcMatchers("/admin/save").permitAll()
                .mvcMatchers("/wx/user/login").permitAll()
                .mvcMatchers("/wx/user/save").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint);
    }
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    /**
     * 配置哪些請求不攔截
     * 排除swagger相關(guān)請求
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/favicon.ico","/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**", "/doc.html");
    }
}

防止報錯還有這個自定義的登錄攔截器跟認(rèn)證失敗處理器也整上

@Component
public class TokenAuthenticationFilter extends OncePerRequestFilter {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    protected void doFilterInternal
            (HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = request.getHeader("token");
        if (ObjectUtils.isEmpty(token)){
            filterChain.doFilter(request,response);
            return;
        }
        Claims claims = null;
        try {
            claims = JwtUtil.parseJWT(token);
        } catch (Exception e) {
            e.printStackTrace();
            Map<String, String> errMsg = new HashMap<>();
            errMsg.put("code","200");
            errMsg.put("msg","訪問失敗,請重新登錄");
            response.setContentType("text/json;charset=utf-8");
            response.getWriter().print(errMsg.toString());
            return;
        }
        Integer userId = Integer.valueOf(claims.getSubject());
        UserContext.setUser(userId);
        String userAdmin = redisTemplate.opsForValue().get("userId" + userId);
        AdminLogin adminLogin = JSONUtil.toBean(userAdmin, AdminLogin.class);
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(adminLogin.getUsername(), adminLogin.getUsername(), null);
//        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(null, null, null);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        filterChain.doFilter(request,response);
    }
}
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable
{
    private static final long serialVersionUID = -8970718410437077606L;
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
            throws IOException
    {
        Map<String, String> errMsg = new HashMap<>();
        response.setContentType("text/json;charset=utf-8");
        errMsg.put("code","200");
        errMsg.put("msg","訪問失敗,該資源受到保護(hù)...");
        response.getWriter().print(errMsg.toString());
    }
}

等會再說這兩個配置

3、繼承UserDetails

用我們的登錄的用戶類繼承UserDetails,我這里是Admin

@Data
@AllArgsConstructor
@NoArgsConstructor
public class AdminLogin implements UserDetails {
    private Admin admin;
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }
    @Override
    public String getPassword() {
        return admin.getPassword();
    }
    @Override
    public String getUsername() {
        return admin.getUsername();
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return true;
    }
}

我們得重寫里面的幾個方法,并且把我們的用戶類給整進(jìn)來

Collection<? extends GrantedAuthority> getAuthorities()

這個是權(quán)限,我們返回null就行了,這會登錄用不到

public String getPassword() {return admin.getPassword();}

這個方法是獲取密碼,也就是security會從這里獲取登錄的密碼,我們就把我們的用戶類的密碼讓他返回

public String getUsername() { return admin.getUsername();}

這個是獲取用戶名的方法,也就是security會從這里獲取登錄的用戶名,我們就把我們的用戶類的用戶名讓他返回

@Override
public boolean isAccountNonExpired() {
    return true;
}
@Override
public boolean isAccountNonLocked() {
    return true;
}
@Override
public boolean isCredentialsNonExpired() {
    return true;
}
@Override
public boolean isEnabled() {
    return true;
}

這幾個都是賬號相關(guān)的,什么賬號是否被鎖定、是否啟用在這里返回結(jié)果,我們返回true,如果返回false就登錄不了了

4、登錄方法

@Service
public class AdminLoginServiceImpl implements AdminLoginService {
    @Autowired
    private AuthenticationManager authenticationManager;
    //管理員登錄
    @Override
    public Result adminLogin(LoginDto loginDto) {
        UsernamePasswordAuthenticationToken authenticationToken = new
                UsernamePasswordAuthenticationToken(loginDto.getUsername(), loginDto.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        AdminLogin adminLogin = (AdminLogin) authenticate.getPrincipal();
        String jwt = JwtUtil.createJWT(String.valueOf(adminLogin.getAdmin().getId()));
        //用戶信息
        redisTemplate.opsForValue().set("userId"+adminLogin.getAdmin().getId(), JSONUtil.toJsonStr(adminLogin));
        return Result.success(jwt);
    }
}

登錄的方法就是調(diào)用

Authentication authenticate = authenticationManager.authenticate(authenticationToken);

它里面需要接受的參數(shù)類型必須是Authentication類型的

這就是為啥在配置的時候?qū)?/p>

AuthenticationManager authenticationManagerBean()這個使用@Bean標(biāo)記

 UsernamePasswordAuthenticationToken authenticationToken = new
                UsernamePasswordAuthenticationToken(loginDto.getUsername(),loginDto.getPassword());

這個類就可以將我們的用戶名和密碼封裝成繼承了Authentication類型的類然后用于登錄

UsernamePasswordAuthenticationToken()他的參數(shù)是

Object principal, Object credentials

這就分別是用戶名和登陸憑證也就是密碼

然后登陸成功后返回一個Authentication類型,然后.getPrincipal()這個方法就可以獲取登錄的用戶信息。

5、是怎么完成登錄的

這時候我們來看登錄流程圖(圖是盜的)

當(dāng)我們調(diào)用Authentication authenticate = authenticationManager.authenticate(authenticationToken);這個方法的時候做了什么?

我們關(guān)注兩步就可以了

第一個就是根據(jù)用戶名查詢用戶,第二個就是進(jìn)行密碼比對

(1)根據(jù)用戶名查詢用戶

因為在執(zhí)行認(rèn)證的方法后,會調(diào)用DaoAuthencationProvider中的UserDetailService對象中的loadUserByUsername這個方法,如果基于springsecurity的默認(rèn)配置,這個方法就是實現(xiàn)了UserDetailService這個接口的InMemoryUserDetailsManager這個方法中的loadUserByUsername

我們可以看到進(jìn)行登錄時候調(diào)用了loadUserByUsername的方法,這個方法是在

UserDetailsService中寫的,看方法名也知道是根據(jù)用戶名查找用戶

因為我們要查的是數(shù)據(jù)庫中的用戶數(shù)據(jù),我們就可以也可以實現(xiàn)UserDetailService并且重寫里面的loadUserByUsername方法   根據(jù)數(shù)據(jù)庫查詢出用戶信息并返回繼承了UserDetail的AdminLogin 類

@Service
public class AdminDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private AdminService adminService;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根據(jù)用戶名查詢數(shù)據(jù)庫中的用戶
        LambdaQueryWrapper<Admin> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Admin::getUsername,username);
        Admin admin = adminService.getOne(wrapper);
        //如果根據(jù)用戶名查找不到用戶
        if (ObjectUtils.isEmpty(admin)){
            throw new TxdException(208,"用戶不存在");
        }
        //返回adminLogin
        AdminLogin adminLogin = new AdminLogin(admin);
        return adminLogin;
    }
}

如果能查到用戶就返回,然后進(jìn)行下一步密碼對比,如果用戶不存在就拋異常

(2)密碼對比

這個springsecurity都已經(jīng)寫好了,我們看看源碼就行,找到那個我們自定義的security的配置類

ctrl點進(jìn)BCryptPasswordEncoder加密方式

里面的boolean matches(CharSequence rawPassword, String encodedPassword)這個方法就是密碼對比,它里面又會執(zhí)行BCrypt.checkpw(rawPassword.toString(), encodedPassword)這個方法。總之就是將前端傳來的密碼進(jìn)行加密后與數(shù)據(jù)庫的進(jìn)行對比

為啥不能將數(shù)據(jù)庫的密碼解析后對比傳來的明文密碼呢?因為他這個加密之后是不可逆的

然后到這登錄基本就完事了

新問題,數(shù)據(jù)庫中還沒加密后的用戶數(shù)據(jù)怎么辦?

6、注冊用戶加密密碼

將前端傳來的密碼使用security配置類中的加密方式加密后就行

7、登錄過濾器

在前面配置的時候已經(jīng)整過代碼了,在這里獲取token并校驗,校驗完之后獲取里面的用戶id,根據(jù)用戶id獲取redis里面的數(shù)據(jù),并將用戶信息使用UsernamePasswordAuthenticationToken 封裝并且放入SecurityContextHolder.getContext().setAuthentication(authenticationToken);中

@Component
public class TokenAuthenticationFilter extends OncePerRequestFilter {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    protected void doFilterInternal
            (HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = request.getHeader("token");
        if (ObjectUtils.isEmpty(token)){
            filterChain.doFilter(request,response);
            return;
        }
        Claims claims = null;
        try {
            claims = JwtUtil.parseJWT(token);
        } catch (Exception e) {
            e.printStackTrace();
            Map<String, String> errMsg = new HashMap<>();
            errMsg.put("code","200");
            errMsg.put("msg","訪問失敗,請重新登錄");
            response.setContentType("text/json;charset=utf-8");
            response.getWriter().print(errMsg.toString());
            return;
        }
        Integer userId = Integer.valueOf(claims.getSubject());
        UserContext.setUser(userId);
        String userAdmin = redisTemplate.opsForValue().get("userId" + userId);
        AdminLogin adminLogin = JSONUtil.toBean(userAdmin, AdminLogin.class);
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(adminLogin.getUsername(), adminLogin.getUsername(), null);
//        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(null, null, null);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        filterChain.doFilter(request,response);
    }
}

8、認(rèn)證失敗處理器

當(dāng)用戶未認(rèn)證時訪問資源提示的信息

@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable
{
    private static final long serialVersionUID = -8970718410437077606L;
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
            throws IOException
    {
        Map<String, String> errMsg = new HashMap<>();
        response.setContentType("text/json;charset=utf-8");
        errMsg.put("code","200");
        errMsg.put("msg","訪問失敗,該資源受到保護(hù)...");
        response.getWriter().print(errMsg.toString());
    }
}

到此這篇關(guān)于springsecurity實現(xiàn)用戶登錄認(rèn)證快速使用(前后端分離項目)的文章就介紹到這了,更多相關(guān)springsecurity用戶登錄認(rèn)證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 深入理解Java注解類型(@Annotation)

    深入理解Java注解類型(@Annotation)

    這篇文章主要介紹了深入理解Java注解類型(@Annotation),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • Java插件擴(kuò)展機(jī)制之SPI案例講解

    Java插件擴(kuò)展機(jī)制之SPI案例講解

    這篇文章主要介紹了Java插件擴(kuò)展機(jī)制之SPI案例講解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • Java多線程下載文件實現(xiàn)案例詳解

    Java多線程下載文件實現(xiàn)案例詳解

    這篇文章主要介紹了Java多線程下載文件實現(xiàn)案例詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-05-05
  • Java中雙大括號初始化的理解與使用

    Java中雙大括號初始化的理解與使用

    最近重讀Java 編程思想,讀到有關(guān)實例化代碼塊兒的內(nèi)容,使我對于使用兩個大括號進(jìn)行初始化有了更深的理解,下面這篇文章主要給大家介紹了關(guān)于Java中雙大括號初始化的理解與使用的相關(guān)資料,需要的朋友可以參考下
    2022-06-06
  • Java中圖像銳化操作的方法詳解

    Java中圖像銳化操作的方法詳解

    這篇文章主要給大家介紹了關(guān)于Java中圖像銳化操作的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • Spring Bean 依賴注入常見錯誤問題

    Spring Bean 依賴注入常見錯誤問題

    這篇文章主要介紹了Spring Bean 依賴注入常見錯誤問題,文中提到value的工作大體分為三個核心步驟,具體內(nèi)容詳情跟隨小編一起看看吧
    2021-09-09
  • java調(diào)用相互依賴的dll的處理方法

    java調(diào)用相互依賴的dll的處理方法

    大家好,本篇文章主要講的是java調(diào)用相互依賴的dll的處理方法,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-01-01
  • 如何通過RabbitMq實現(xiàn)動態(tài)定時任務(wù)詳解

    如何通過RabbitMq實現(xiàn)動態(tài)定時任務(wù)詳解

    工作中經(jīng)常會有定時任務(wù)的需求,常見的做法可以使用Timer、Quartz、Hangfire等組件,這次想嘗試下新的思路,使用RabbitMQ死信隊列的機(jī)制來實現(xiàn)定時任務(wù),下面這篇文章主要給大家介紹了關(guān)于如何通過RabbitMq實現(xiàn)動態(tài)定時任務(wù)的相關(guān)資料,需要的朋友可以參考下
    2022-01-01
  • Java 線程池原理深入分析

    Java 線程池原理深入分析

    這篇文章主要介紹了Java 線程池原理深入分析的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • 使用SpringBoot設(shè)置虛擬路徑映射絕對路徑

    使用SpringBoot設(shè)置虛擬路徑映射絕對路徑

    這篇文章主要介紹了使用SpringBoot設(shè)置虛擬路徑映射絕對路徑的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08

最新評論