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

Java中Spring的Security使用詳解

 更新時間:2023年07月31日 11:35:29   作者:兔老大RabbitMQ  
這篇文章主要介紹了Java中Spring的Security使用詳解,在web應(yīng)用開發(fā)中,安全無疑是十分重要的,選擇Spring Security來保護web應(yīng)用是一個非常好的選擇,需要的朋友可以參考下

Spring Security

在web應(yīng)用開發(fā)中,安全無疑是十分重要的,選擇Spring Security來保護web應(yīng)用是一個非常好的選擇。

Spring Security 是spring項目之中的一個安全模塊,可以非常方便與spring項目無縫集成。

特別是在spring boot項目中加入spring security更是十分簡單。

本篇我們介紹spring security,以及spring security在web應(yīng)用中的使用。

一個例子入門

假設(shè)我們現(xiàn)在創(chuàng)建好了一個springboot的web應(yīng)用,有一個控制器如下:

@Controller
public class AppController {
 @RequestMapping("/hello")
 @ResponseBody
 String home() {
 return "Hello ,spring security!";
 }
}

我們啟動應(yīng)用,假設(shè)端口是8080,那么當我們在瀏覽器訪問//localhost:8080/hello的時候可以在瀏覽器看到Hello ,spring security!。

加入spring security 保護應(yīng)用

此時,/hello是可以自由訪問。假設(shè),我們需要具有某個角色的用戶才能訪問的時候,我們可以引入spring security來進行保護。加入如下maven依賴,并重啟應(yīng)用:

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

再次訪問/hello,我們可以得到一個http-basic的認證彈窗,如下:

代碼如下:

<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
<h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
<table>
 <tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
 <tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
 <tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
 <input name="_csrf" type="hidden" value="635780a5-6853-4fcd-ba14-77db85dbd8bd" />
</table>
</form></body></html>

我們可以發(fā)現(xiàn),這里有個form 。action=”/login”,這個/login是spring security提供的。

form表單提交了三個數(shù)據(jù):

  • username 用戶名   
  • password 密碼   
  • _csrf CSRF保護方面的內(nèi)容

說明spring security 已經(jīng)起作用了。這時,它為你生成了賬號和密碼,在內(nèi)存中。

但是,我們不可能只是這么使用它,我們?nèi)绾瓮ㄟ^訪問數(shù)據(jù)庫來登錄,驗證權(quán)限呢?

接下來通過一個實例繼續(xù)深入。

demo項目權(quán)限介紹

我們通過一個很簡單的項目來認識一下Spring Security。

  • index.html:社區(qū)首頁(只有四個鏈接)----------任何人都可以訪問
  • discuss.html:帖子詳情頁面(只有一句話)------任何人都可以訪問
  • letter.html:私信列表(只有一句話)-------只有登陸后的用戶才能訪問
  • admin.html:管理員頁面(只有一句話)----只有管理員才能訪問
  • login.html:登陸頁面(有表單)----------------不符合要求時,可以登錄

我們的目的,就是把這些權(quán)限管理起來。

靜態(tài)頁面代碼展示

下面展示一下這些頁面的代碼:

index.html:社區(qū)首頁(只有四個鏈接)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首頁</title>
</head>
<body>
    <h1>社區(qū)首頁</h1>
    <ul>
        <li><a th:href="@{/discuss}" rel="external nofollow" >帖子詳情</a></li>
        <li><a th:href="@{/letter}" rel="external nofollow" >私信列表</a></li>
        <li><a th:href="@{/loginpage}" rel="external nofollow"  rel="external nofollow" >登錄</a></li>
        <li><a th:href="@{/loginpage}" rel="external nofollow"  rel="external nofollow" >退出</a></li>
    </ul>
</body>
</html>

discuss.html:帖子詳情頁面(只有一句話)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>帖子</title>
</head>
<body>
    <h1>帖子詳情頁面</h1>
</body>
</html>

letter.html:私信列表(只有一句話)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>私信</title>
</head>
<body>
    <h1>私信列表頁面</h1>
</body>
</html>

admin.html:管理員頁面(只有一句話)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>管理員</title>
</head>
<body>
    <h1>管理員專屬頁面</h1>
</body>
</html>

login.html:登陸頁面(有表單)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登錄</title>
</head>
<body>
    <h1>登錄社區(qū)</h1>
    <form method="post" action="#">
        <p style="color:red;">
            <!--提示信息-->
        </p>
        <p>
            賬號:<input type="text" >
        </p>
        <p>
            密碼:<input type="password" >
        </p>
        <p>
            驗證碼:<input type="text" >
        </p>
        <p>
            <input type="submit" value="登錄">
        </p>
    </form>
</body>
</html>

service層和user的操作

首先要處理我們的用戶user表,寫出獲取權(quán)限的方法。

實現(xiàn)UserDetails 接口,和接口定義的方法。

方法的作用我都已經(jīng)標注在代碼里。

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
public class User implements UserDetails {
    private int id;
    private String username;
    private String password;
    private String salt;
    private String email;
    private int type;
    private int status;
    private String activationCode;
    private String headerUrl;
    private Date createTime;
/*
get/set方法
toSring方法
*/
    // true: 賬號未過期.
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    // true: 賬號未鎖定.
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    // true: 憑證未過期.
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    // true: 賬號可用.
    @Override
    public boolean isEnabled() {
        return true;
    }
    // 返回用戶權(quán)限
    //我們有兩種用戶:1代表管理員,2代表普通用戶
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> list = new ArrayList<>();
        list.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                switch (type) {
                    case 1:
                        return "ADMIN";
                    default:
                        return "USER";
                }
            }
        });
        return list;
    }
}

著重介紹一下getAuthorities方法:

它的返回值是一個權(quán)限集合,因為我們真實開發(fā)可能是這樣的:

  • 用戶表:記錄了用戶類型(是普通用戶還是1級管理員、2級管理員、賣家買家等等)
  • 權(quán)限表:記錄了每個角色有什么權(quán)限,比如普通用戶可以發(fā)帖評論點贊,管理員可以刪貼置頂?shù)鹊取?/li>

最后我們通過用戶類型,查到多種權(quán)限,并且返回。

因為每種用戶有多種權(quán)限,所以getAuthorities方法的返回值是一個權(quán)限集合。,這個集合可以裝很多GrantedAuthority對象。

本代碼的集合只裝了一個權(quán)限對象,并且重寫了對應(yīng)回去權(quán)限的方法。。

service層實現(xiàn)接口和對應(yīng)方法

import com.nowcoder.community.dao.UserMapper;
import com.nowcoder.community.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class UserService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    public User findUserByName(String username) {
        return userMapper.selectByName(username);
    }
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return this.findUserByName(username);
    }
}

這個接口是要實現(xiàn)查找對應(yīng)的用戶。

我們自己寫登錄邏輯的時候,一樣要這么做:用賬號(id)查到用戶,讀取密碼,看看用戶輸入的和在數(shù)據(jù)庫查到的是否相同,

security底層也是做了類似的事情,所以我們需要告訴他如何查找用戶。

如果我們之前寫過selectByName之類的方法,可以直接調(diào)用即可。

這里我把dao層和xml實現(xiàn)也給出來。

import com.nowcoder.community.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
    User selectByName(String username);
}
<mapper namespace="com.community.dao.UserMapper">
    <sql id="selectFields">
        id, username, password, salt, email, type, status, activation_code, header_url, create_time
    </sql>
    <select id="selectByName" resultType="User">
        select
        <include refid="selectFields"></include>
        from user
        where username = #{username}
    </select>
</mapper>

核心操作

書寫配置類統(tǒng)一管理。

有詳細的注釋。

通常我們需要書寫如下代碼:

忽略哪些資源?

認證

授權(quán)

package com.community.config;
import com.community.entity.User;
import com.community.service.UserService;
import com.community.util.CommunityUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserService userService;
    @Override
    public void configure(WebSecurity web) throws Exception {
        // 忽略靜態(tài)資源的訪問
        web.ignoring().antMatchers("/resources/**");
    }
    // AuthenticationManager: 認證的核心接口.
    // AuthenticationManagerBuilder: 用于構(gòu)建AuthenticationManager對象的工具.
    // ProviderManager: AuthenticationManager接口的默認實現(xiàn)類.
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 內(nèi)置的認證規(guī)則
        // auth.userDetailsService(userService).passwordEncoder(new Pbkdf2PasswordEncoder("12345"));
        // 自定義認證規(guī)則
        // AuthenticationProvider: ProviderManager持有一組AuthenticationProvider,每個AuthenticationProvider負責一種認證.
        // 委托模式: ProviderManager將認證委托給AuthenticationProvider.
        auth.authenticationProvider(new AuthenticationProvider() {
            // Authentication: 用于封裝認證信息的接口,不同的實現(xiàn)類代表不同類型的認證信息.
            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                String username = authentication.getName();
                String password = (String) authentication.getCredentials();
                User user = userService.findUserByName(username);
                if (user == null) {
                    throw new UsernameNotFoundException("賬號不存在!");
                }
                password = CommunityUtil.md5(password + user.getSalt());
                if (!user.getPassword().equals(password)) {
                    throw new BadCredentialsException("密碼不正確!");
                }
                // principal: 主要信息; credentials: 證書; authorities: 權(quán)限;
                return new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
            }
            // 當前的AuthenticationProvider支持哪種類型的認證.
            @Override
            public boolean supports(Class<?> aClass) {
                // UsernamePasswordAuthenticationToken: Authentication接口的常用的實現(xiàn)類.
                return UsernamePasswordAuthenticationToken.class.equals(aClass);
            }
        });
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 登錄相關(guān)配置
        http.formLogin()
                .loginPage("/loginpage")
                .loginProcessingUrl("/login")
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.sendRedirect(request.getContextPath() + "/index");
                    }
                })
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
                        request.setAttribute("error", e.getMessage());
                        request.getRequestDispatcher("/loginpage").forward(request, response);
                    }
                });
        // 退出相關(guān)配置
        http.logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.sendRedirect(request.getContextPath() + "/index");
                    }
                });
        // 授權(quán)配置
        http.authorizeRequests()
                .antMatchers("/letter").hasAnyAuthority("USER", "ADMIN")
                .antMatchers("/admin").hasAnyAuthority("ADMIN")
                .and().exceptionHandling().accessDeniedPage("/denied");
        // 增加Filter,處理驗證碼
        http.addFilterBefore(new Filter() {
            @Override
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                HttpServletRequest request = (HttpServletRequest) servletRequest;
                HttpServletResponse response = (HttpServletResponse) servletResponse;
                if (request.getServletPath().equals("/login")) {
                    String verifyCode = request.getParameter("verifyCode");
                    if (verifyCode == null || !verifyCode.equalsIgnoreCase("1234")) {
                        request.setAttribute("error", "驗證碼錯誤!");
                        request.getRequestDispatcher("/loginpage").forward(request, response);
                        return;
                    }
                }
                // 讓請求繼續(xù)向下執(zhí)行.
                filterChain.doFilter(request, response);
            }
        }, UsernamePasswordAuthenticationFilter.class);
        // 記住我
        http.rememberMe()
                .tokenRepository(new InMemoryTokenRepositoryImpl())
                .tokenValiditySeconds(3600 * 24)
                .userDetailsService(userService);
    }
}
  • 表單
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登錄</title>
</head>
<body>
    <h1>登錄社區(qū)</h1>
    <form method="post" th:action="@{/login}">
        <p style="color:red;" th:text="${error}">
            <!--提示信息-->
        </p>
        <p>
            賬號:<input type="text" name="username" th:value="${param.username}">
        </p>
        <p>
            密碼:<input type="password" name="password" th:value="${param.password}">
        </p>
        <p>
            驗證碼:<input type="text" name="verifyCode"> <i>1234</i>
        </p>
        <p>
            <input type="checkbox" name="remember-me"> 記住我
        </p>
        <p>
            <input type="submit" value="登錄">
        </p>
    </form>
</body>
</html>
  • HomeController 
package com.nowcoder.community.controller;
import com.nowcoder.community.entity.User;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class HomeController {
    @RequestMapping(path = "/index", method = RequestMethod.GET)
    public String getIndexPage(Model model) {
        // 認證成功后,結(jié)果會通過SecurityContextHolder存入SecurityContext中.
        Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (obj instanceof User) {
            model.addAttribute("loginUser", obj);
        }
        return "/index";
    }
    @RequestMapping(path = "/discuss", method = RequestMethod.GET)
    public String getDiscussPage() {
        return "/site/discuss";
    }
    @RequestMapping(path = "/letter", method = RequestMethod.GET)
    public String getLetterPage() {
        return "/site/letter";
    }
    @RequestMapping(path = "/admin", method = RequestMethod.GET)
    public String getAdminPage() {
        return "/site/admin";
    }
    @RequestMapping(path = "/loginpage", method = {RequestMethod.GET, RequestMethod.POST})
    public String getLoginPage() {
        return "/site/login";
    }
    // 拒絕訪問時的提示頁面
    @RequestMapping(path = "/denied", method = RequestMethod.GET)
    public String getDeniedPage() {
        return "/error/404";
    }
}

到此這篇關(guān)于Java中Spring的Security使用詳解的文章就介紹到這了,更多相關(guān)Spring的Security內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺析Java類和數(shù)據(jù)結(jié)構(gòu)中常用的方法

    淺析Java類和數(shù)據(jù)結(jié)構(gòu)中常用的方法

    下面小編就為大家?guī)硪黄獪\析Java類和數(shù)據(jù)結(jié)構(gòu)中常用的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-09-09
  • java redis 實現(xiàn)簡單的用戶簽到功能

    java redis 實現(xiàn)簡單的用戶簽到功能

    這篇文章主要介紹了java redis 實現(xiàn)簡單的用戶簽到功能,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-12-12
  • springboot項目使用SchedulingConfigurer實現(xiàn)多個定時任務(wù)的案例代碼

    springboot項目使用SchedulingConfigurer實現(xiàn)多個定時任務(wù)的案例代碼

    這篇文章主要介紹了springboot項目使用SchedulingConfigurer實現(xiàn)多個定時任務(wù),本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-01-01
  • 什么是Java多線程,如何實現(xiàn)

    什么是Java多線程,如何實現(xiàn)

    這篇文章主要給大家介紹了關(guān)于實現(xiàn)Java多線程的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用java具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧吧
    2021-11-11
  • springboot?自定義啟動器的實現(xiàn)

    springboot?自定義啟動器的實現(xiàn)

    本文主要介紹了springboot?自定義啟動器的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-02-02
  • Java SSM整合開發(fā)統(tǒng)一結(jié)果封裝詳解

    Java SSM整合開發(fā)統(tǒng)一結(jié)果封裝詳解

    這篇文章主要介紹了Java SSM整合開發(fā)實現(xiàn)統(tǒng)一結(jié)果封裝,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-08-08
  • Java?ThreadPoolExecutor線程池有關(guān)介紹

    Java?ThreadPoolExecutor線程池有關(guān)介紹

    這篇文章主要介紹了Java?ThreadPoolExecutor線程池有關(guān)介紹,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-09-09
  • 詳解Java變量與常量

    詳解Java變量與常量

    這篇文章主要介紹了Java變量與常量,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-04-04
  • java繼承中的構(gòu)造方法實例解析

    java繼承中的構(gòu)造方法實例解析

    這篇文章主要介紹了java繼承中的構(gòu)造方法實例解析,針對繼承中的構(gòu)造方法的特點進行了實例分析,需要的朋友可以參考下
    2014-10-10
  • SpringBoot中開啟GZIP壓縮的方式

    SpringBoot中開啟GZIP壓縮的方式

    這篇文章主要介紹了SpringBoot中開啟GZIP壓縮的方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-04-04

最新評論