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

SpringBoot整合SpringSecurity認(rèn)證與授權(quán)

 更新時(shí)間:2023年11月10日 10:41:16   作者:全棧小白.  
在項(xiàng)目開發(fā)中,權(quán)限認(rèn)證是很重要的,尤其是一些管理類的系統(tǒng),對(duì)于權(quán)限要求更為嚴(yán)格,本文主要介紹了SpringBoot整合SpringSecurity認(rèn)證與授權(quán),感興趣的可以了解一下

嘮嗑部分

在項(xiàng)目開發(fā)中,權(quán)限認(rèn)證是很重要的,尤其是一些管理類的系統(tǒng),對(duì)于權(quán)限要求更為嚴(yán)格,那么在Java開發(fā)中,常用的權(quán)限框架有哪些呢?

推薦的有兩種,Shiro 與 SpringSecurity,當(dāng)然也可以結(jié)合切面自己實(shí)現(xiàn)

Shiro是Apache開源的一款權(quán)限框架,比較輕量級(jí),簡(jiǎn)單容易學(xué),但是不能在其中注入Spring中的容器Bean

SpringSecurity是Spring生態(tài)中的一個(gè)組件,比較重量級(jí),它也整合了OAuth2協(xié)議,對(duì)于Spring框架來說,更推薦SpringSecurity

今天我們就來分享一下如何整合SpringSecurity進(jìn)行認(rèn)證與授權(quán),順便實(shí)現(xiàn)一下token的無感知續(xù)期

SpringSecurity進(jìn)行認(rèn)證與授權(quán)是SpringSecurity框架進(jìn)行處理,我們就不必多說,按照步驟進(jìn)行編碼就OK了

token的無感知續(xù)期我們來說一下思路:

1、用戶在登錄成功后,由服務(wù)器下發(fā)token,有效期30分鐘。

2、客戶端拿到token之后,請(qǐng)求其余需要認(rèn)證的接口時(shí),再請(qǐng)求頭攜帶token訪問。

3、服務(wù)器編寫過濾器,對(duì)請(qǐng)求頭中的token進(jìn)行驗(yàn)證,判斷用戶登錄是否有效,于此同時(shí),判斷token有效期是否即將過期,如果即將過期,重新頒發(fā)token,如果已過期,返回401未認(rèn)證狀態(tài)碼。

上面說到判斷token有效期是否即將過期,說明一下哈

oauth2中是頒發(fā)了兩個(gè)token,一個(gè)access_token(訪問token),一個(gè)refresh_token(刷新token),刷新token的有效期是訪問token的2倍,如果訪問token過期,就拿刷新token重新申請(qǐng)?jiān)L問token

我們只有一個(gè)token,邏輯是判斷token的有效期,如果有效期小于15分鐘,就刷新token,這樣的話既可以實(shí)現(xiàn)toekn的無感知刷新

實(shí)際的token是比較長(zhǎng)的一段字符串,標(biāo)準(zhǔn)的jwt token包括頭部、載荷、簽名,格式如下

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxIiwiaWQiOiIxIiwiYXV0aGVudGljYXRpb25zIjpbImFkbWluIl0sInVzZXJuYW1lIjoiYWRtaW4iLCJpYXQiOjE2ODg2MjI3MjMsImV4cCI6MTY4ODYyNDUyM30.xPRul6ePE1bwSe70rbo0-jPFUxU9O9MPQf9gliZ18X8

接口設(shè)計(jì)說明:

1、用戶認(rèn)證處理器

接口地址:/auth/login

請(qǐng)求方式:POST

請(qǐng)求頭:content-type: application/json;charset=utf-8

接口功能說明:進(jìn)行用戶認(rèn)證,頒發(fā)token,實(shí)際的token比較長(zhǎng),我們是生成了一個(gè)字符串充當(dāng)token,實(shí)際token存在于redis中

接口參數(shù):

{<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->"password": "","username": ""}

出參說明:

tokenInfo:token信息,包括token的失效時(shí)間、token值、用戶信息等等

user:用戶信息

{
    "code": 200,
    "data": {
        "tokenInfo": {
            "expirationTime": "2023-07-06 12:25:42",
            "token": "3f49e86b93c743f2865a4446a7a85398",
            "user": {
                "authentications": [
                    "ROLE_user"
                ],
                "id": "2",
                "username": "user"
            }
        },
        "user": {
            "roles": [
                "ROLE_user"
            ],
            "userId": "2",
            "userStatus": 1,
            "userType": 0,
            "username": "admin"
        }
    },
    "msg": "登陸成功"
}

2、用戶令牌檢查處理器

接口地址:/auth/checkToken

請(qǐng)求方式:POST

請(qǐng)求頭: X-Access-Token

接口功能說明:token令牌檢查

出參說明:

status: 檢查結(jié)果

token:token信息

{
    "code": 200,
    "data": {
        "status": true,
        "token": {
            "expirationTime": "2023-07-06 12:25:42",
            "token": "3f49e86b93c743f2865a4446a7a85398",
            "user": {
                "authentications": [
                    "ROLE_user"
                ],
                "id": "2",
                "username": "user"
            }
        }
    },
    "msg": "操作成功"
}

3、測(cè)試接口

/admin/common/test:只有admin角色才能訪問

/common/test:任何角色都可以訪問

言歸正傳

1、相關(guān)SQL腳本

create database `springsecurity_case` character set 'utf8mb4';
use `springsecurity_case`;

create table t_user(
    user_id varchar(50) primary key comment '用戶id',
    username varchar(50) not null comment '用戶名',
    password varchar(100) not null comment '密碼',
    role varchar(50) not null comment '角色',
    user_status tinyint(1) default 1 comment '用戶狀態(tài)'
);

insert into t_user values ('1', 'admin', '$2a$10$wmUXgiTZzc3ux3h3UiuxWumeDYbt8uaZmmPw6utx9GyyuGEDSTNJy', 'admin', 1),
                          ('2', 'user', '$2a$10$wmUXgiTZzc3ux3h3UiuxWumeDYbt8uaZmmPw6utx9GyyuGEDSTNJy', 'user', 1);

2、創(chuàng)建項(xiàng)目&導(dǎo)入依賴

<!--  ... 常規(guī)化的依賴省略了-->
<!--        redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!--        springSecurity-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

3、UserDetailService實(shí)現(xiàn)類的編寫,認(rèn)證的主邏輯

/*
 * @Project:springboot-springsecurity-case
 * @Author:cxs
 * @Motto:放下雜念,只為迎接明天更好的自己
 * */
@Service
@Slf4j
public class UserDetailServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if (!StringUtils.hasLength(username)) {
            throw new UsernameNotFoundException("用戶名豈能為空!");
        }
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        User user = userMapper.selectOne(queryWrapper);

        if (ObjectUtils.isEmpty(user)) {
            throw new UsernameNotFoundException("用戶名不存在");
        }

        if (user.getUserStatus().equals(2)) {
            throw new LockedException("賬戶已被鎖定,認(rèn)證失敗");
        }

        GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_" + user.getRole());
        Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        grantedAuthorities.add(grantedAuthority);
        return new AuthUser(user.getUserId(), username, user.getPassword(), grantedAuthorities);
    }
}

4、Token過濾器的編寫

/*
 * @Project:springboot-springsecurity-case
 * @Author:cxs
 * @Motto:放下雜念,只為迎接明天更好的自己
 * */
@Component
public class TokenVerificationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private CommonConfig commonConfig;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        // 獲取url,如果在核心配置文件中配置了白名單,則跳過驗(yàn)證
        String requestURI = request.getRequestURI();
        if (ignore(requestURI)) {
            filterChain.doFilter(request, response);
            return;
        }
        // 獲取X-Access-Token
        String header = request.getHeader(CommonContent.TOKEN);
        if (!StringUtils.hasLength(header)) {
            filterChain.doFilter(request, response);
            return;
        }
        Token token = null;
        List<String> strings = null;
        try {
            // 根據(jù)X-Access-Token去redis查詢真實(shí)token
            String tokenStr = redisUtil.getString(redisUtil.getCacheKey(CachePrefixContent.TOKEN_PREFIX, header.trim()));
            if (!StringUtils.hasLength(tokenStr)) {
                response(response, BaseResult.error().setCode(ResponseStateConstant.NO_LOGIN).setMsg("用戶認(rèn)證信息已過期"));
                return;
            }
            if (jwtUtil.validTokenIssued(tokenStr)) {
                response(response, BaseResult.error().setCode(ResponseStateConstant.NO_LOGIN).setMsg("用戶認(rèn)證信息已過期"));
                return;
            }
            // 校驗(yàn)信息是否正確,省略
            token = jwtUtil.parseToken(tokenStr);
            strings = token.getUser().getAuthentications();
            Authentication context = new UsernamePasswordAuthenticationToken(
                    token.getUser().getUsername(),
                    token.getUser().getUsername(),
                    AuthorityUtils.createAuthorityList(strings.toArray(new String[0]))
            );
            SecurityContextHolder.getContext().setAuthentication(context);
            Long expire = redisUtil.getExpire(redisUtil.getCacheKey(CachePrefixContent.TOKEN_PREFIX, header));
            // 有效期小于15分鐘,續(xù)時(shí)
            if (expire <= 900L) {
                redisUtil.set(redisUtil.getCacheKey(CachePrefixContent.TOKEN_PREFIX, header), jwtUtil.generateToken(token.getUser()), commonConfig.getValidityTime(), TimeUnit.MINUTES);
            }
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            if (e instanceof SignatureException) {
                BaseResult error = BaseResult.error();
                error.setMsg(CurrencyErrorEnum.UNAUTHORIZED.getMsg());
                error.setCode(CurrencyErrorEnum.UNAUTHORIZED.getCode());
                response(response, error);
            } else if (e instanceof ExpiredJwtException) {
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                BaseResult error = BaseResult.error();
                error.setMsg(CurrencyErrorEnum.UNAUTHORIZED_BE_OVERDUE.getMsg());
                error.setCode(CurrencyErrorEnum.UNAUTHORIZED_BE_OVERDUE.getCode());
                response(response, error);
            } else if (e instanceof JwtException) {
                BaseResult error = BaseResult.error();
                error.setMsg(CurrencyErrorEnum.UNAUTHORIZED.getMsg());
                error.setCode(CurrencyErrorEnum.UNAUTHORIZED.getCode());
                response(response, error);
            } else {
                throw e;
            }
        }
    }
    // ...
}

5、Security核心配置文件編寫

/*
 * @Project:springboot-springsecurity-case
 * @Author:cxs
 * @Motto:放下雜念,只為迎接明天更好的自己
 * */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /*
    創(chuàng)建加密編碼器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    private TokenVerificationFilter tokenVerificationFilter;

    @Autowired
    private AccessForbiddenHandler forbiddenHandler;

    @Autowired
    private AuthenticationHandler authenticationHandler;

    @Autowired
    private CommonConfig commonConfig;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 請(qǐng)求授權(quán)管理
        http.authorizeRequests()
                // 其他的請(qǐng)求都需要授權(quán)
                .antMatchers(commonConfig.getIgnoreUrl()).permitAll()
                .antMatchers("/common/**").hasAnyRole("admin", "user")
                .antMatchers("/admin/**").hasRole("admin")
                .anyRequest().authenticated()
                .and()
                .exceptionHandling()
                .accessDeniedHandler(forbiddenHandler)
                .authenticationEntryPoint(authenticationHandler)
                .and()
                .csrf().disable()
                // 整合token校驗(yàn)過濾器
                .addFilterBefore(tokenVerificationFilter, UsernamePasswordAuthenticationFilter.class)
                // 禁用springsecurity的本地存儲(chǔ),做無狀態(tài)登錄
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    /**
     * 注入認(rèn)證管理器
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }
}

6、登錄認(rèn)證邏輯

這一塊有兩個(gè)邏輯

1、首先如果用戶帶著X-Access-Token進(jìn)行登錄,首先判斷是否有效,如果有效的話,直接將之前的token回傳給用戶

2、如果失效、或者用戶未帶X-Access-Token,進(jìn)行登錄邏輯

public void login(UserLoginDTO dto, HttpServletRequest request, HttpServletResponse response, BaseResult result) {
    // 判斷用戶是否攜帶X-Access-Token
    String accessTokenKey = request.getHeader(CommonContent.TOKEN);
    if (StringUtils.hasLength(accessTokenKey)) {
        String tokenStr = redisUtil.getString(redisUtil.getCacheKey(CachePrefixContent.TOKEN_PREFIX, accessTokenKey.trim()));
        Token token = null;
        try {
            token = jwtUtil.parseToken(tokenStr);
        } catch (Exception e) {
            log.info("用戶登錄:用戶已有token校驗(yàn)失敗");
        }
        // 進(jìn)行token驗(yàn)證
        if (!ObjectUtils.isEmpty(token)) {
            String generateToken = jwtUtil.generateToken(token.getUser());
            redisUtil.set(redisUtil.getCacheKey(CachePrefixContent.TOKEN_PREFIX, accessTokenKey), generateToken, commonConfig.getValidityTime(), TimeUnit.MINUTES);
            Token parseToken = jwtUtil.parseToken(generateToken);
            if (!ObjectUtils.isEmpty(parseToken)) parseToken.setToken(accessTokenKey);
            UserLoginVO vo = new UserLoginVO();
            vo.setTokenInfo(parseToken);
            UserVO userVO = new UserVO();
            User userInfo = userMapper.selectById(parseToken.getUser().getId());
            BeanUtils.copyProperties(userInfo, userVO);
            userVO.setRoles(parseToken.getUser().getAuthentications());
            vo.setUser(userVO);
            result.setCode(ResponseStateConstant.OPERA_SUCCESS).setData(vo).setMsg("登陸成功");
            return;
        }
    }
    // 如果失效,或者未帶X-Access-Token,進(jìn)行登錄邏輯
    String password = dto.getPassword().trim();
    UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(dto.getUsername().trim(), password);
    Authentication authenticate = null;
    try {
        authenticate = authenticationManager.authenticate(token);
    } catch (AuthenticationException e) {
        e.printStackTrace();
        result.setCode(ResponseStateConstant.OPERA_FAIL).setMsg(e.getMessage());
    }
    if (authenticate != null) {
        UserLoginVO vo = new UserLoginVO();
        SecurityContextHolder.getContext().setAuthentication(authenticate);
        Object principal = authenticate.getPrincipal();
        AuthUser user = (AuthUser) principal;
        List<String> auths = CollectionUtils.isEmpty(user.getAuthorities()) ? new ArrayList<>(0) :
        user.getAuthorities().stream().map(a -> a.getAuthority()).collect(Collectors.toList());
        // 用戶登陸成功,生成token
        String tokenStr = IdUtil.simpleUUID();
        String generateToken = jwtUtil.generateToken(UserSubject.builder()
                                                     .id(user.getId())
                                                     .username(user.getUsername())
                                                     .authentications(auths).build());
        redisUtil.set(redisUtil.getCacheKey(CachePrefixContent.TOKEN_PREFIX, tokenStr), generateToken, commonConfig.getValidityTime(), TimeUnit.MINUTES);
        Token parseToken = jwtUtil.parseToken(generateToken);
        if (!ObjectUtils.isEmpty(parseToken)) parseToken.setToken(tokenStr);
        vo.setTokenInfo(parseToken);
        UserVO userVO = new UserVO();
        User userInfo = userMapper.selectById(user.getId());
        BeanUtils.copyProperties(userInfo, userVO);
        userVO.setRoles(auths);
        vo.setUser(userVO);
        result.setCode(ResponseStateConstant.OPERA_SUCCESS).setData(vo).setMsg("登陸成功");
    }
}

7、測(cè)試

使用user角色登錄

image-20230706142601489

使用token分別訪問測(cè)試接口

image-20230706143252046

使用admin角色登錄

image-20230706143329522

使用token分別訪問測(cè)試接口

image-20230706143457124

token令牌檢查接口測(cè)試

image-20230706143545026

查看真實(shí)token,在redis存儲(chǔ)

image-20230706144019683

到此這篇關(guān)于SpringBoot整合SpringSecurity認(rèn)證與授權(quán)的文章就介紹到這了,更多相關(guān)SpringBoot SpringSecurity認(rèn)證與授權(quán)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot整合vue實(shí)現(xiàn)上傳下載文件

    springboot整合vue實(shí)現(xiàn)上傳下載文件

    這篇文章主要為大家詳細(xì)介紹了springboot整合vue實(shí)現(xiàn)上傳下載文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-11-11
  • 學(xué)習(xí)Java之異常到底該如何捕獲和處理

    學(xué)習(xí)Java之異常到底該如何捕獲和處理

    我們知道,Java的異常處理是通過5個(gè)關(guān)鍵字來實(shí)現(xiàn)的,即try、catch、throw、throws和finally,try?catch語句用于捕獲并處理異常,但具體該怎么捕獲異常,怎么拋出異常,什么時(shí)候拋,什么時(shí)候捕,感興趣的小伙伴跟著小編一起來看看吧
    2023-08-08
  • Java利用JavaCPP調(diào)用算法示例

    Java利用JavaCPP調(diào)用算法示例

    本文主要介紹了Java利用JavaCPP調(diào)用算法示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • Java兩種方式實(shí)現(xiàn)動(dòng)態(tài)代理

    Java兩種方式實(shí)現(xiàn)動(dòng)態(tài)代理

    Java 在 java.lang.reflect 包中有自己的代理支持,該類(Proxy.java)用于動(dòng)態(tài)生成代理類,只需傳入目標(biāo)接口、目標(biāo)接口的類加載器以及 InvocationHandler 便可為目標(biāo)接口生成代理類及代理對(duì)象。我們稱這個(gè)Java技術(shù)為:動(dòng)態(tài)代理
    2020-10-10
  • 劍指Offer之Java算法習(xí)題精講數(shù)組與列表的查找及字符串轉(zhuǎn)換

    劍指Offer之Java算法習(xí)題精講數(shù)組與列表的查找及字符串轉(zhuǎn)換

    跟著思路走,之后從簡(jiǎn)單題入手,反復(fù)去看,做過之后可能會(huì)忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會(huì)發(fā)現(xiàn)質(zhì)的變化
    2022-03-03
  • Java 和 Scala 如何調(diào)用變參

    Java 和 Scala 如何調(diào)用變參

    這篇文章主要介紹了Java 和 Scala 如何調(diào)用變參,幫助大家更好的理解和學(xué)習(xí)Java與Scala,感興趣的朋友可以了解下
    2020-09-09
  • logback 實(shí)現(xiàn)給變量指定默認(rèn)值

    logback 實(shí)現(xiàn)給變量指定默認(rèn)值

    這篇文章主要介紹了logback 實(shí)現(xiàn)給變量指定默認(rèn)值操作,具有很好的參考家價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java語言實(shí)現(xiàn)簡(jiǎn)單FTP軟件 FTP遠(yuǎn)程文件管理模塊實(shí)現(xiàn)(10)

    Java語言實(shí)現(xiàn)簡(jiǎn)單FTP軟件 FTP遠(yuǎn)程文件管理模塊實(shí)現(xiàn)(10)

    這篇文章主要為大家詳細(xì)介紹了Java語言實(shí)現(xiàn)簡(jiǎn)單FTP軟件,F(xiàn)TP遠(yuǎn)程文件管理模塊的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • SpringBoot整合RabbitMQ處理死信隊(duì)列和延遲隊(duì)列

    SpringBoot整合RabbitMQ處理死信隊(duì)列和延遲隊(duì)列

    這篇文章將通過示例為大家詳細(xì)介紹SpringBoot整合RabbitMQ時(shí)如何處理死信隊(duì)列和延遲隊(duì)列,文中的示例代碼講解詳細(xì),需要的可以參考一下
    2022-05-05
  • MyBatis?Generator?ORM層面的代碼自動(dòng)生成器(推薦)

    MyBatis?Generator?ORM層面的代碼自動(dòng)生成器(推薦)

    Mybatis?Generator是一個(gè)專門為?MyBatis和?ibatis框架使用者提供的代碼生成器,也可以快速的根據(jù)數(shù)據(jù)表生成對(duì)應(yīng)的pojo類、Mapper接口、Mapper文件,甚至生成QBC風(fēng)格的查詢對(duì)象,這篇文章主要介紹了MyBatis?Generator?ORM層面的代碼自動(dòng)生成器,需要的朋友可以參考下
    2023-01-01

最新評(píng)論