最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Security 單點(diǎn)登錄與自動(dòng)登錄機(jī)制的實(shí)現(xiàn)原理

 更新時(shí)間:2025年07月26日 16:36:12   作者:從零開始學(xué)習(xí)人工智能  
本文探討SpringSecurity實(shí)現(xiàn)單點(diǎn)登錄(SSO)與自動(dòng)登錄機(jī)制,涵蓋JWT跨系統(tǒng)認(rèn)證、RememberMe持久化Token存儲(chǔ)、安全工具類及最佳實(shí)踐,強(qiáng)調(diào)安全性與性能平衡對(duì)提升用戶體驗(yàn)的重要性,感興趣的朋友一起看看吧

在現(xiàn)代企業(yè)級(jí)應(yīng)用中,用戶需要訪問多個(gè)相關(guān)但獨(dú)立的系統(tǒng)。傳統(tǒng)的每次訪問都需要重新登錄的方式不僅用戶體驗(yàn)差,而且安全性也難以保障。本文將深入探討基于Spring Security的單點(diǎn)登錄(SSO)和自動(dòng)登錄機(jī)制的實(shí)現(xiàn)原理。

一、核心概念解析

1.1 單點(diǎn)登錄(SSO)

單點(diǎn)登錄是指用戶只需要登錄一次,就可以訪問所有相互信任的應(yīng)用系統(tǒng)。

1.2 自動(dòng)登錄(Remember Me)

自動(dòng)登錄是指用戶在一定時(shí)間內(nèi)無需重復(fù)輸入用戶名密碼即可自動(dòng)完成身份認(rèn)證。

二、代碼分析

讓我們先分析一下提供的代碼片段:

// 1. 手動(dòng)查詢用戶
SysUser sysUser = userService.selectUserByUserName(username);
if (sysUser == null) {
    throw new UsernameNotFoundException("用戶不存在");
}
// 3. 查詢權(quán)限
Set<String> permissions = sysPermissionService.getMenuPermission(sysUser);
// 4. 構(gòu)造LoginUser對(duì)象
LoginUser loginUser = new LoginUser(sysUser.getUserId(),sysUser.getDeptId(),sysUser, permissions);
// 4. 構(gòu)造已認(rèn)證的Authentication對(duì)象
authentication = new UsernamePasswordAuthenticationToken(
        loginUser,           // principal - 這里傳遞的是完整的LoginUser對(duì)象
        null,                // credentials
        loginUser.getAuthorities() // authorities
);
// 5. 設(shè)置到Security上下文
SecurityContextHolder.getContext().setAuthentication(authentication);
Long userId = SecurityUtils.getUserId();

這段代碼展示了手動(dòng)構(gòu)建認(rèn)證信息的核心流程。

三、單點(diǎn)登錄實(shí)現(xiàn)方案

3.1 基于JWT的SSO實(shí)現(xiàn)

@Component
public class JwtTokenProvider {
    @Value("${jwt.secret}")
    private String secret;
    @Value("${jwt.expiration}")
    private Long expiration;
    public String generateToken(LoginUser loginUser) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expiration);
        return Jwts.builder()
                .setSubject(loginUser.getUsername())
                .claim("userId", loginUser.getUserId())
                .claim("permissions", loginUser.getPermissions())
                .setIssuedAt(new Date())
                .setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
    public String validateTokenAndGetUserId(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
        return claims.get("userId", String.class);
    }
}

3.2 SSO認(rèn)證過濾器

@Component
public class SsoAuthenticationFilter extends OncePerRequestFilter {
    @Autowired
    private JwtTokenProvider tokenProvider;
    @Autowired
    private UserService userService;
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                  HttpServletResponse response, 
                                  FilterChain filterChain) throws ServletException, IOException {
        String token = getJwtFromRequest(request);
        if (StringUtils.hasText(token) && tokenProvider.validateToken(token)) {
            try {
                String userId = tokenProvider.validateTokenAndGetUserId(token);
                SysUser sysUser = userService.selectUserById(userId);
                if (sysUser != null) {
                    Set<String> permissions = sysPermissionService.getMenuPermission(sysUser);
                    LoginUser loginUser = new LoginUser(sysUser.getUserId(), 
                                                       sysUser.getDeptId(), 
                                                       sysUser, 
                                                       permissions);
                    UsernamePasswordAuthenticationToken authentication = 
                        new UsernamePasswordAuthenticationToken(
                            loginUser, 
                            null, 
                            loginUser.getAuthorities()
                        );
                    authentication.setDetails(
                        new WebAuthenticationDetailsSource().buildDetails(request)
                    );
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            } catch (Exception ex) {
                logger.error("Could not set user authentication in security context", ex);
            }
        }
        filterChain.doFilter(request, response);
    }
    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

四、自動(dòng)登錄機(jī)制實(shí)現(xiàn)

4.1 RememberMe配置

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private PersistentTokenRepository persistentTokenRepository;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/login", "/public/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .loginPage("/login")
            .and()
            .rememberMe()
            .rememberMeParameter("remember-me")
            .tokenRepository(persistentTokenRepository)
            .tokenValiditySeconds(86400) // 24小時(shí)
            .userDetailsService(userDetailsService);
    }
}

4.2 持久化Token存儲(chǔ)

@Component
public class PersistentTokenRepositoryImpl implements PersistentTokenRepository {
    @Autowired
    private RememberMeTokenMapper rememberMeTokenMapper;
    @Override
    public void createNewToken(PersistentRememberMeToken token) {
        RememberMeToken entity = new RememberMeToken();
        entity.setSeries(token.getSeries());
        entity.setUsername(token.getUsername());
        entity.setToken(token.getTokenValue());
        entity.setLastUsed(token.getDate());
        rememberMeTokenMapper.insert(entity);
    }
    @Override
    public void updateToken(String series, String tokenValue, Date lastUsed) {
        RememberMeToken entity = new RememberMeToken();
        entity.setSeries(series);
        entity.setToken(tokenValue);
        entity.setLastUsed(lastUsed);
        rememberMeTokenMapper.updateByPrimaryKey(entity);
    }
    @Override
    public PersistentRememberMeToken getTokenForSeries(String seriesId) {
        RememberMeToken entity = rememberMeTokenMapper.selectByPrimaryKey(seriesId);
        if (entity != null) {
            return new PersistentRememberMeToken(
                entity.getUsername(),
                entity.getSeries(),
                entity.getToken(),
                entity.getLastUsed()
            );
        }
        return null;
    }
    @Override
    public void removeUserTokens(String username) {
        rememberMeTokenMapper.deleteByUsername(username);
    }
}

五、完整登錄服務(wù)實(shí)現(xiàn)

@Service
public class SysLoginService {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtTokenProvider tokenProvider;
    @Autowired
    private UserService userService;
    @Autowired
    private SysPermissionService sysPermissionService;
    /**
     * 用戶登錄
     */
    public String login(String username, String password, String code, String uuid) {
        // 1. 驗(yàn)證碼校驗(yàn)
        validateCaptcha(code, uuid);
        // 2. 用戶認(rèn)證
        Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(username, password)
        );
        // 3. 認(rèn)證成功后生成JWT Token
        SecurityContextHolder.getContext().setAuthentication(authentication);
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        return tokenProvider.generateToken(loginUser);
    }
    /**
     * 自動(dòng)登錄處理
     */
    public String autoLogin(String token) {
        if (StringUtils.hasText(token) && tokenProvider.validateToken(token)) {
            String userId = tokenProvider.validateTokenAndGetUserId(token);
            SysUser sysUser = userService.selectUserById(userId);
            if (sysUser != null) {
                Set<String> permissions = sysPermissionService.getMenuPermission(sysUser);
                LoginUser loginUser = new LoginUser(sysUser.getUserId(), 
                                                   sysUser.getDeptId(), 
                                                   sysUser, 
                                                   permissions);
                UsernamePasswordAuthenticationToken authentication = 
                    new UsernamePasswordAuthenticationToken(
                        loginUser, 
                        null, 
                        loginUser.getAuthorities()
                    );
                SecurityContextHolder.getContext().setAuthentication(authentication);
                // 生成新的token
                return tokenProvider.generateToken(loginUser);
            }
        }
        throw new AuthenticationException("自動(dòng)登錄失敗");
    }
    private void validateCaptcha(String code, String uuid) {
        // 驗(yàn)證碼校驗(yàn)邏輯
        String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
        String captcha = redisCache.getCacheObject(verifyKey);
        redisCache.deleteObject(verifyKey);
        if (captcha == null || !code.equalsIgnoreCase(captcha)) {
            throw new CaptchaException("驗(yàn)證碼錯(cuò)誤");
        }
    }
}

六、安全工具類

public class SecurityUtils {
    /**
     * 獲取用戶ID
     */
    public static Long getUserId() {
        try {
            return getLoginUser().getUserId();
        } catch (Exception e) {
            throw new CustomException("獲取用戶ID異常", HttpStatus.UNAUTHORIZED);
        }
    }
    /**
     * 獲取登錄用戶信息
     */
    public static LoginUser getLoginUser() {
        try {
            return (LoginUser) getAuthentication().getPrincipal();
        } catch (Exception e) {
            throw new CustomException("獲取用戶信息異常", HttpStatus.UNAUTHORIZED);
        }
    }
    /**
     * 獲取Authentication
     */
    public static Authentication getAuthentication() {
        return SecurityContextHolder.getContext().getAuthentication();
    }
}

七、最佳實(shí)踐建議

7.1 安全性考慮

  1. Token過期時(shí)間:合理設(shè)置JWT過期時(shí)間
  2. Token刷新機(jī)制:實(shí)現(xiàn)Token刷新避免頻繁登錄
  3. HTTPS傳輸:確保Token在傳輸過程中的安全

7.2 性能優(yōu)化

  1. 緩存機(jī)制:對(duì)用戶權(quán)限信息進(jìn)行緩存
  2. 異步處理:將非關(guān)鍵業(yè)務(wù)異步處理
  3. 數(shù)據(jù)庫(kù)優(yōu)化:對(duì)RememberMe表建立合適的索引

7.3 監(jiān)控和日志

@Component
public class LoginLogAspect {
    @Around("execution(* com.example.service.SysLoginService.login(..))")
    public Object logLogin(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = null;
        try {
            result = joinPoint.proceed();
            // 記錄成功日志
            logLoginSuccess(joinPoint.getArgs());
            return result;
        } catch (Exception e) {
            // 記錄失敗日志
            logLoginFailure(joinPoint.getArgs(), e);
            throw e;
        } finally {
            long endTime = System.currentTimeMillis();
            logger.info("登錄耗時(shí): {}ms", endTime - startTime);
        }
    }
}

八、總結(jié)

通過本文的介紹,我們了解了:

  1. 單點(diǎn)登錄的核心原理:基于JWT實(shí)現(xiàn)跨系統(tǒng)認(rèn)證
  2. 自動(dòng)登錄的實(shí)現(xiàn)機(jī)制:RememberMe和持久化Token存儲(chǔ)
  3. Spring Security集成:如何與現(xiàn)有安全框架整合
  4. 最佳實(shí)踐:安全性和性能方面的考慮

在實(shí)際項(xiàng)目中,需要根據(jù)業(yè)務(wù)需求選擇合適的方案,并注意安全性和性能的平衡。單點(diǎn)登錄和自動(dòng)登錄機(jī)制的合理運(yùn)用,能夠顯著提升用戶體驗(yàn)和系統(tǒng)安全性。

到此這篇關(guān)于Spring Security 單點(diǎn)登錄與自動(dòng)登錄機(jī)制的實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)Spring Security 單點(diǎn)登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于Java開發(fā)一個(gè)Markdown到Word文檔轉(zhuǎn)換工具

    基于Java開發(fā)一個(gè)Markdown到Word文檔轉(zhuǎn)換工具

    本文介紹了一個(gè)用Java開發(fā)的Markdown到Word文檔轉(zhuǎn)換工具,該工具通過MarkdownToWordConverter類實(shí)現(xiàn),利用flexmark庫(kù)將Markdown內(nèi)容轉(zhuǎn)換為HTML,借助jsoup庫(kù)規(guī)范化HTML,再通過docx4j庫(kù)將處理后的HTML導(dǎo)入并保存為Word文檔,需要的朋友可以參考下
    2025-07-07
  • Elasticsearch?計(jì)數(shù)分詞中的token使用實(shí)例

    Elasticsearch?計(jì)數(shù)分詞中的token使用實(shí)例

    這篇文章主要為大家介紹了Elasticsearch?計(jì)數(shù)分詞中的token使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • 通過Java實(shí)現(xiàn)添加或刪除PDF中的附件

    通過Java實(shí)現(xiàn)添加或刪除PDF中的附件

    當(dāng)我們?cè)谥谱鱌DF文件或者PPT演示文稿的時(shí)候,為了讓自己的文件更全面詳細(xì),就會(huì)在文件中添加附件。本文為大家整理了Java實(shí)現(xiàn)添加或刪除PDF中的附件的方法,需要的可以參考下
    2023-01-01
  • Java反射如何獲取字段屬性值

    Java反射如何獲取字段屬性值

    這篇文章主要介紹了Java反射如何獲取字段屬性值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • Java常用工具類匯總 附示例代碼

    Java常用工具類匯總 附示例代碼

    這篇文章主要介紹了Java常用工具類匯總 附示例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • mybatisplus下劃線駝峰轉(zhuǎn)換的問題解決

    mybatisplus下劃線駝峰轉(zhuǎn)換的問題解決

    在mybatis-plus中,下劃線-駝峰自動(dòng)轉(zhuǎn)換可能導(dǎo)致帶下劃線的字段查詢結(jié)果為null,本文就來介紹一下mybatisplus下劃線駝峰轉(zhuǎn)換的問題解決,感興趣的可以了解一下
    2024-10-10
  • IntelliJ IDEA右鍵文件夾沒有Java Class文件的原因及解決方法

    IntelliJ IDEA右鍵文件夾沒有Java Class文件的原因及解決方法

    這篇文章主要介紹了IntelliJ IDEA右鍵文件夾沒有Java Class文件的原因及解決方法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • SpringBoot + openFeign實(shí)現(xiàn)遠(yuǎn)程接口調(diào)用的過程

    SpringBoot + openFeign實(shí)現(xiàn)遠(yuǎn)程接口調(diào)用的過程

    現(xiàn)在的微服務(wù)項(xiàng)目不少都使用的是springboot+spring cloud構(gòu)建的項(xiàng)目,微服務(wù)之間的調(diào)用都離不開feign來進(jìn)行遠(yuǎn)程調(diào)用,這篇文章主要介紹了SpringBoot + openFeign實(shí)現(xiàn)遠(yuǎn)程接口調(diào)用,需要的朋友可以參考下
    2022-11-11
  • rocketmq如何修改存儲(chǔ)路徑

    rocketmq如何修改存儲(chǔ)路徑

    這篇文章主要介紹了rocketmq如何修改存儲(chǔ)路徑的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 基于SpringBoot從零構(gòu)建生產(chǎn)級(jí)OOM監(jiān)測(cè)預(yù)警系統(tǒng)

    基于SpringBoot從零構(gòu)建生產(chǎn)級(jí)OOM監(jiān)測(cè)預(yù)警系統(tǒng)

    OOM是Java應(yīng)用在生產(chǎn)環(huán)境中最致命的殺手之一,一次未及時(shí)發(fā)現(xiàn)的內(nèi)存泄漏,足以讓整個(gè)服務(wù)集群在數(shù)分鐘內(nèi)崩潰,造成不可估量的業(yè)務(wù)損失,本文將從技術(shù)原理到工程實(shí)踐,帶你構(gòu)建一套完整的Spring Boot OOM監(jiān)測(cè)預(yù)警系統(tǒng),需要的朋友可以參考下
    2026-03-03

最新評(píng)論

思茅市| 沙洋县| 普宁市| 樟树市| 江川县| 涪陵区| 田林县| 东海县| 清河县| 金堂县| 岳池县| 晋江市| 磐石市| 兴城市| 南宁市| 临漳县| 广东省| 页游| 昌江| 武穴市| 陆河县| 康保县| 河间市| 德清县| 宣汉县| 易门县| 从江县| 台南县| 库伦旗| 济源市| 含山县| 田林县| 成武县| 天门市| 宿州市| 大同市| 星座| 青川县| 沁阳市| 呼伦贝尔市| 闽侯县|