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

SpringSecurity實(shí)現(xiàn)自定義登錄接口的詳細(xì)過程

 更新時(shí)間:2024年10月09日 10:18:22   作者:o獨(dú)酌o  
本文詳細(xì)介紹了如何使用SpringSecurity實(shí)現(xiàn)自定義登錄接口,文章還涉及了對(duì)用戶實(shí)體類的增強(qiáng)以滿足詳細(xì)信息的需求,適合需要深入了解和實(shí)現(xiàn)SpringSecurity自定義登錄功能的開發(fā)者,感興趣的朋友跟隨小編一起看看吧

SpringSecurity實(shí)現(xiàn)自定義登錄接口

1、配置類 ConfigClazz(SpringSecuriey的)

    //首先就是要有一個(gè)配置類
	@Resource
    private DIYUsernamePasswordAuthenticationFilter diyUsernamePasswordAuthenticationFilter;
    /*SpringSecurity配置*/
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeRequests(
                    authorize -> authorize
                            .requestMatchers("/user/**","/").hasRole("user") //擁有user的角色可訪問的接口
                            .requestMatchers("/manager/**").hasRole("manager")//擁有manager的角色可訪問的接口
                            .requestMatchers("/login/**").permitAll()
                            .anyRequest() 
                            .authenticated() // 任何請(qǐng)求都需要授權(quán),重定向到
            );
        /*登錄頁*/
		http.formLogin(AbstractHttpConfigurer::disable);//禁用默認(rèn)的登錄接口,使用自定義的登錄接口
        /*登出*/
        http.logout(logout ->{
            logout
                    .logoutUrl("/goOut").permitAll()
                    //登錄退出成功,向前端返回json格式的字符串
                    .logoutSuccessHandler((HttpServletRequest request, HttpServletResponse response, Authentication authentication)->{
                        Map<String, String[]> parameterMap = request.getParameterMap();
                        //進(jìn)入登錄頁時(shí),判斷是否已經(jīng)登陸過 TowLogin 參數(shù)
                        if(!parameterMap.isEmpty() && parameterMap.get("TowLogin")[0].equals("true")){
                            String json = JSON.toJSONString(Code.NOTowLogin);
                            response.setContentType("application/json;charset=UTF-8");
                            response.getWriter().println(json);
                        } else {
                            String json = JSON.toJSONString(Code.SuccessLogout);
                            response.setContentType("application/json;charset=UTF-8");
                            response.getWriter().println(json);
                        }
                    });
        });
        /*向過濾器鏈中添加自定義的過濾器
          用自定義的過濾器代替 UsernamePasswordAuthenticationFilter 過濾器
        */
        http.addFilterAfter(diyUsernamePasswordAuthenticationFilter, LogoutFilter.class);
        /*請(qǐng)求異常處理*/
        http.exceptionHandling(exception ->{
            /*用戶未登錄時(shí),訪問限權(quán)接口,返回 json 格式的字符串
            這個(gè)配是。把頁面跳轉(zhuǎn)交給前端,即:用戶未登錄時(shí),后端只返回 json 格式的字符串,不會(huì)跳轉(zhuǎn)頁面
            -- 未登錄時(shí),重定向的 url  .loginPage("/login/getLoginHTML").permitAll(),就不起作用了 --
            */
            exception.authenticationEntryPoint((HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)->{
                String json = JSON.toJSONString(Code.NoLogin);
                response.setContentType("application/json;charset=UTF-8");
                response.getWriter().println(json);
            });
            //響應(yīng)登錄用戶訪問未授權(quán)路徑時(shí)(user角色訪問manager角色的接口) 有 未授權(quán) json 提示
            exception.accessDeniedHandler((HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)->{
                String json = JSON.toJSONString(Code.Forbidden);
                response.setContentType("application/json;charset=UTF-8");
                response.getWriter().println(json);
            });
        });
        /*會(huì)話管理*/
        http.sessionManagement(session -> {
            session
                    //表示,最大連接數(shù)量為 1 ,同一個(gè)賬號(hào),最多只能在一臺(tái)設(shè)備上登錄,當(dāng)?shù)诙€(gè)登陸時(shí),會(huì)把第一個(gè)擠掉
                    .maximumSessions(1)
                    //擠掉后,對(duì)前端返回的json字符串
                    .expiredSessionStrategy((SessionInformationExpiredEvent event)->{
                        String json = JSON.toJSONString(Code.ForeignLogin);
                        HttpServletResponse response = event.getResponse();
                        response.setContentType("application/json;charset=UTF-8");
                        response.getWriter().println(json);
                    });
        });
        /*開啟跨域訪問*/
        http.cors(withDefaults());
       /* 禁用csrf的防御手段。
        * 開啟后,相當(dāng)于每次前端訪問接口的時(shí)候
        * 都需要攜帶_crsf為參數(shù)名的參數(shù),功能類似于 token,
        * 因此建議禁用
        * */
        http.csrf(AbstractHttpConfigurer::disable);
        return http.build();
    }
	//設(shè)置密碼的編碼方式(必須有)
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder(10); 
    }

解釋 _scrf 在哪看,只有最初有,后面就沒有,但是如果不攜帶,就不讓你訪問接口,因此建議禁用

2、DIYUsernamePasswordAuthenticationFilter

該類用于替換 UsernamePasswordAuthenticationFilter 過濾器,應(yīng)用自己自定義的過濾器

@Component  //相當(dāng)于 UsernamePasswordAuthenticationFilter
public class DIYUsernamePasswordAuthenticationFilter extends OncePerRequestFilter {
@Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        /*問題:不能讀取請(qǐng)求體中的信息,因?yàn)槭且淮涡缘?,讀完,后面就不能用了
        * 因此,這里避免用json格式傳輸 賬號(hào) 和 密碼
        * */
        //獲取非 json 格式傳輸?shù)?,OK了,只要前端給 json 格式 的token就能獲取了
        Map<String, String[]> parameterMap = request.getParameterMap(); //有前端打開
        SUser user = null;
        HttpSession session = request.getSession();
        //有前端打開
        //檢查token,通過token解析出用戶的賬號(hào),根據(jù)賬號(hào),從 session 中查詢
        if(parameterMap.get("token") != null)
            user = (SUser)session.getAttribute(parameterMap.get("token")[0]);
        if (user == null) {
            //放行,表示已經(jīng)退出,需要重新驗(yàn)證,區(qū)別就是有沒有 存入SecurityContextHolder 一步驟
            filterChain.doFilter(request, response);
            return;
        }
        //存入SecurityContextHolder,獲取權(quán)限信息封裝到Authentication中
        UsernamePasswordAuthenticationToken authenticationToken =  // 沒有前端獲取用戶數(shù)據(jù)目前先這樣寫
                new UsernamePasswordAuthenticationToken(user,user.getPassword(),user.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        //驗(yàn)證成功,放行
        filterChain.doFilter(request, response);
    }
}

3、DIYAuthenticationProvider

該類是發(fā)放授權(quán)的接口

@Component
public class DIYAuthenticationProvider implements AuthenticationProvider {
    @Resource
    private UserDetailsService userDetailsService;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = (String) authentication.getCredentials();
        // 從數(shù)據(jù)庫中加載用戶信息
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        // 檢查密碼是否正確
        if (!passwordEncoder.matches(password, userDetails.getPassword())) {
            throw new BadCredentialsException("用戶名或密碼錯(cuò)誤");
        }
        // 創(chuàng)建一個(gè)已認(rèn)證的 Authentication 對(duì)象
        UsernamePasswordAuthenticationToken authenticatedToken =
                new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
        authenticatedToken.setDetails(authentication.getDetails());
        return authenticatedToken;
    }
    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

4、DIYAuthenticationManager

該類是用來調(diào)用發(fā)放授權(quán)接口

@Component
public class DIYAuthenticationManager implements AuthenticationManager {
    @Resource  //這里雖然是注入的接口,但是由于自定義的類 DIYAuthenticationProvider 實(shí)現(xiàn)了該接口,因此優(yōu)先使用
    AuthenticationProvider authenticationProvider;
      		  //這里其實(shí)可以調(diào)用默認(rèn)的 授權(quán)提供者,有匹配的就會(huì)授權(quán),但是,沒必要,因?yàn)榭隙ㄆヅ洳涣?,最后還是用自己的,
    @Override //那不如  直接就用自己的就好了
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        return authenticationProvider.authenticate(authentication);
    }
}

5、MySQLUserDetailsManager

該類用于獲取用戶的信息

@Component  //將這個(gè)類交給Spring容器管理,即:創(chuàng)建該類的 bean 對(duì)象,進(jìn)而取代(重寫)原來的方法
public class MySQLUserDetailsManager implements UserDetailsService{
    //由于是基于數(shù)據(jù)庫的,因此,只需要實(shí)現(xiàn)一個(gè) UserDetailsService 接口就好,不需要實(shí)現(xiàn)其他的接口
    @Resource //這個(gè)是Mapper接口,用于從數(shù)據(jù)庫中調(diào)用查詢信息
    SUserMapper sUserMapper; 
    @Resource  //這個(gè)是必要的
    HttpServletRequest request; 
    @Override                           //String username
    public UserDetails loadUserByUsername(String account) throws UsernameNotFoundException {
        //獲取數(shù)據(jù)信息要在這里開始,由于只暴露用戶輸入account,因此數(shù)據(jù)庫中的數(shù)據(jù)只能,所有的 account都不一樣,才能唯一匹配 account,這里 Email 一定不一樣
        //這里的 username 就是用戶輸入的賬號(hào),為了方便,就換一個(gè)變量名 account
        List<SUser> sUsers = sUserMapper.selectAllByEmail(account);  //這里 Email 一定不一樣
        if(sUsers != null && !sUsers.isEmpty()) {
            SUser sUser = sUsers.get(0);
            //這里把 authenticate 這個(gè)用戶的信息存到session中,如果調(diào)用退出登錄接口,就會(huì)刪除session里面的內(nèi)容
            HttpSession session = request.getSession();
            session.setAttribute(String.valueOf(sUser.getEmail()),sUser);
            return sUser;
        } else {
            throw new UsernameNotFoundException(account);
        }
    }
}

6、控制層

@Controller
@Tag(name = "登錄注冊(cè)")
@RequestMapping("/login")
public class LoginController {
    @Resource
    private SUserService sUserService;
    @Resource
    private AuthenticationManager authenticationManager;
    @GetMapping("/getLoginHTML")  //進(jìn)入登錄頁的接口
    public String getLoginHtml(HttpSession session){
        boolean aNew = session.isNew();
        if(aNew)
            return "login";
        //如果一個(gè)瀏覽器試圖登錄兩次,那么就會(huì)直接調(diào)用退出接口
        return "redirect:/goOut?TowLogin=true";
    }
    @PostMapping("/ooo")  //由于是自定義登錄接口,因此什么請(qǐng)求都可以,建議用Post
    @ResponseBody  //將返回值寫入響應(yīng)體中
    public Code login(String account,String password){
        SUser sUser = new SUser();
        sUser.setEmail(account);
        sUser.setPassword(password);
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(sUser,password);
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        if(Objects.isNull(authenticate))
            throw new AuthenticationCredentialsNotFoundException("用戶賬號(hào)或密碼錯(cuò)誤");
        else{
            //這里響應(yīng)回去一個(gè) token,根據(jù)賬號(hào)加密后,生成的 token
            Map<String, String> map = new HashMap<>();
            map.put("token",authenticate.getName());
            return new Code<>(Code.OK, map);
        }
}

7、增強(qiáng)用戶的實(shí)體類

這里由于要封裝用戶的詳細(xì)信息,而用 MybatisX 生成的 User 實(shí)體類不能滿足需求,因此要實(shí)現(xiàn)一個(gè)接口

@TableName(value ="s_user")
@Data
@Repository  //將這個(gè)類交給IOC容器(Spring)管理
public class SUser implements Serializable , UserDetails{  //實(shí)現(xiàn)這個(gè)接口
    /**
     * 主鍵id,自動(dòng)遞增
     */
    @TableId(type = IdType.AUTO)
    private Integer id;
    /**
     * 用戶名:<=10
     */
    private String name;
    /**
     * 年齡
     */
    private Integer age;
    /**
     * 性別:女 , 男
     */
    private String sex;
    /**
     * 郵箱賬號(hào):<=30
     */
    private String email;
    /**
     * 密碼:<=15
     */
    private String password;
    /**
     * 是否被禁用:0-未禁用,1-已禁用
     */
    private Integer isForbidden;
    /**
     * 該賬號(hào)的角色:0-普通用戶,1-管理員
     */
    private String role;
    /**
     * 是否被刪除(或用戶注銷):0-未刪除,1-刪除
     */
    @TableLogic
    private Integer isDelete;
    @Serial
    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        /*這里要自己拼接 ROLE_ + role
        * ROLE_ : 是固定的
        * 由于我這里的實(shí)體類設(shè)計(jì)的是:String role; 不是數(shù)組形式,因此不用循環(huán)
        * 如果是數(shù)組形式的限權(quán),循環(huán)遍歷,并創(chuàng)建 SimpleGrantedAuthority 就好了
        * */
        List<SimpleGrantedAuthority> list  = new ArrayList<>();
        list.add(new SimpleGrantedAuthority("ROLE_" + role));
        return list;
    }
    @Override  //注意:這里的用戶名是 賬號(hào)
    public String getUsername() {
        return this.email;
    }
    @Override//沒有這個(gè)設(shè)定就返回通過的結(jié)果,可以用翻譯 isAccountNonExpired ? 在每個(gè)方法名后加一個(gè)? 問自己是true/false
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override//自己的實(shí)體類中有這個(gè)設(shè)定,就返回判斷的結(jié)果
    public boolean isAccountNonLocked() {
        return isForbidden == 1;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return true;
    }
}

8、依賴

java版本 17

springBoot版本 3.2.0

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--SpringSecurity依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--thymeleaf作為視圖模板-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!--mybatis-Puls的依賴-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.4.1</version>
            <!--由于SpringBoot的版本太高,需要這樣1-->
            <exclusions>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis-spring</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--由于SpringBoot的版本太高,需要這樣2-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>3.0.3</version>
        </dependency>
        <!--mysql的驅(qū)動(dòng)包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        <!--簡化實(shí)體類開發(fā)-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--JavaWeb組件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--引入json數(shù)據(jù)依賴,用于給前端返回json類型的數(shù)據(jù)-->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.37</version>
        </dependency>
        <!--knife4j測(cè)試,對(duì)請(qǐng)求的測(cè)試,有兩種,swagger-ui.html / doc.html 都可以-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
            <version>4.4.0</version>
        </dependency>
    </dependencies>

到此這篇關(guān)于SpringSecurity實(shí)現(xiàn)自定義登錄接口的文章就介紹到這了,更多相關(guān)SpringSecurity自定義登錄接口內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論