黄色网址大全免费-黄色网址你懂得-黄色网址你懂的-黄色网址有那些-免费超爽视频-免费大片黄国产在线观看

專注Java教育14年 全國咨詢/投訴熱線:400-8080-105
動力節點LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁 hot資訊 Shiro實現單點登錄的代碼

Shiro實現單點登錄的代碼

更新時間:2022-06-21 11:42:24 來源:動力節點 瀏覽1592次

1.什么是單點登陸

簡單來講,就是在一個系統登陸過后,進入其他系統不需要再次登陸,具體舉個例子來講,在訪問業務B系統時,由于沒有登陸過,先跳到單點登陸A系統進行登陸,在A系統登陸完成之后,跳回到業務B系統的首頁,與此同時,直接訪問業務C系統不需要進行登陸

2.單點登陸實現的原理

用戶訪問頁面會在服務端都會產生一個Session,同時在瀏覽器也需要把這個Session對應的SessionID保存下來,如果登陸過后就會給這個Session綁定上用戶信息。

Session的能在任何系統產生,但是進行用戶信息的綁定需要在單點登陸A系統進行。在訪問單點登陸A系統或者業務B,C系統時,都會從瀏覽器把SessionID帶到服務器,服務器在攔截器通過SessionID獲取Session,如果獲取不到Session或者Session無效就會重定向到單點登陸A系統的登陸頁面。

瀏覽器保存SessionID的方式放在Cookie里面,優點是客戶端對此無感知,缺點是Cookie和域名存在綁定關系,必須放在一級域名下面放在LocalStorage,請求的時候放在url后面或者header里面都可在shiro中主要使用cookie存放sessionid,不過也兼容放在url里面的形式。如果想了解更多相關知識,可以關注一下SSO單點登錄實現的工作原理

3.結合shiro實現單點登陸系統

先說下單點登陸A系統的實現,該系統主要提供一個登陸頁面,登陸成功后會給當前Session綁定用戶信息,Session存儲在redis中,這樣其他子系統也能通過SessionID獲取到

先看下登陸頁面的代碼

<html xmlns:th="http://www.thymeleaf.org">
<head>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
    <p>hello world</p>
    <form>
        <input type="text" id="username" name="username">
        <input type="password" id="password" name="password"/>
        <input type="hidden" id="redirectUrl" th:value="${redirectUrl}"/>
        <input type="submit" id="loginButton" value="登錄"/>
    </form>
    <script>
        $(function () {
            $('#loginButton').click(function (event) {
                event.preventDefault()
                var username = $('#username').val();
                var password = $('#password').val();
                var redirectUrl = $('#redirectUrl').val();
                $.post("/login",{
                    username:username,
                    password:password
                },function (result) {
                    console.log(JSON.stringify(result));
                    if(result.flag==true){
                        window.location.href=redirectUrl;
                    }
                },"json")
            })
        })
    </script>
</body>
</html>

該頁面會把登陸前的頁面保存下來,一旦調用登陸接口成功,通過window.location.href=redirectUrl進行回跳

看下登陸接口的實現

@PostMapping("/login")
    @ResponseBody
    public WebResult login(@RequestParam("username")String username,@RequestParam("password")String password){
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,password);
        try {
            Subject subject = SecurityUtils.getSubject();
            subject.login(usernamePasswordToken);
        }catch(Exception ex){
            logger.error("登錄失敗",ex);
            return new WebResult(null,false);
        }
        return new WebResult(null,true);
    }

通過subject.login進行登陸驗證,成功后會把用戶信息綁定到Session,login方法底層會通過我們配置的AuthenticatingRealm實現進行登陸驗證

public class AuthenticationRealm extends AuthenticatingRealm{
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken usernamePasswordToken =(UsernamePasswordToken)authenticationToken;
        if("scj".equals(usernamePasswordToken.getUsername())&&"123456".equals(new String(usernamePasswordToken.getPassword()))){
            Principal principal = new Principal();
            principal.setUserId(1L);
            principal.setUsername("盛超杰");
            principal.setTelephone("13388611621");
            return new SimpleAuthenticationInfo(principal,((UsernamePasswordToken) authenticationToken).getPassword(),getName());
        }
        throw new IncorrectCredentialsException("賬戶名或密碼錯誤");
    }
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof UsernamePasswordToken;
    }
}

同時Session保存在Redis中,我們通過繼承AbstractSessionDAO實現RedisSessionDAO來完成這個功能

public class RedisSessionDAO extends AbstractSessionDAO{
    private static final String REDIS_SESSION_KEY ="SSO:REDIS_SESSION_KEY";
    private StringRedisTemplate stringRedisTemplate;
    private Serialization serialization;
    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = this.generateSessionId(session);
        this.assignSessionId(session, sessionId);
        stringRedisTemplate.execute(new RedisCallback<Object>() {
            @Nullable
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                connection.hSet(REDIS_SESSION_KEY.getBytes(),sessionId.toString().getBytes(),serialization.seralize(session));
                return null;
            }
        });
        return sessionId;
    }
    @Override
    protected Session doReadSession(Serializable serializable) {
        return (Session) stringRedisTemplate.execute(new RedisCallback<Object>() {
            @Nullable
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] bytes = connection.hGet(REDIS_SESSION_KEY.getBytes(),serializable.toString().getBytes());
                return serialization.deseralize(bytes);
            }
        });
    }
    @Override
    public void update(Session session) throws UnknownSessionException {
        stringRedisTemplate.execute(new RedisCallback<Object>() {
            @Nullable
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                connection.hSet(REDIS_SESSION_KEY.getBytes(),session.getId().toString().getBytes(),serialization.seralize(session));
                return null;
            }
        });
    }
    @Override
    public void delete(Session session) {
        stringRedisTemplate.opsForHash().delete(REDIS_SESSION_KEY,session.getId().toString());
    }
    @Override
    public Collection<Session> getActiveSessions() {
        List<Session> sessionList = new ArrayList<>();
        Set<Object> keys = stringRedisTemplate.opsForHash().keys(REDIS_SESSION_KEY);
        for (Object key:keys){
            sessionList.add((Session) stringRedisTemplate.execute(new RedisCallback<Object>() {
                @Nullable
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    byte[] bytes = connection.hGet(REDIS_SESSION_KEY.getBytes(),key.toString().getBytes());
                    return serialization.deseralize(bytes);
                }
            }));
        }
        return sessionList;
    }
    public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }
    public void setSerialization(Serialization serialization) {
        this.serialization = serialization;
    }
}

在來講下被單點登陸控制的子系統,它們都需要引入ShiroFilter對需要進行登陸驗證的請求進行攔截,這邊對ShiroFilter對配置進行了抽象,由于是用了Springboot,所以配置也沒用xml,使用java類的配置

@Configuration
public abstract class AbstractShiroConfig {
   @Value("${sso.successUrl}")
   private String successUrl;
   @Value("${sso.loginUrl}")
   private String loginUrl;
   @Value("${sso.cookie.domain}")
   private String cookieDomain;
   @Bean
   public FilterRegistrationBean filterRegistrationBean(){
       FilterRegistrationBean filterRegistrationBean =new FilterRegistrationBean();
       filterRegistrationBean.setFilter(new DelegatingFilterProxy());
       filterRegistrationBean.setName("shiroFilter");
       filterRegistrationBean.addUrlPatterns("/*");
       filterRegistrationBean.addInitParameter("targetFilterLifecycle","true");
       return filterRegistrationBean;
   }
   @Bean
   public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
       ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
       shiroFilterFactoryBean.setSecurityManager(securityManager);
       shiroFilterFactoryBean.setSuccessUrl(successUrl);
       shiroFilterFactoryBean.setLoginUrl(loginUrl);
       shiroFilterFactoryBean.setFilterChainDefinitionMap(buildFilterChainDefinitionMap());
       return shiroFilterFactoryBean;
   }
   public abstract Map<String, String> buildFilterChainDefinitionMap();
   @Bean
   public SecurityManager securityManager(SessionManager sessionManager){
       DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
       securityManager.setSessionManager(sessionManager);
       securityManager.setRealm(new AuthenticationRealm());
       return securityManager;
   }
   @Bean
   public SessionManager sessionManager(SimpleCookie simpleCookie,SessionDAO sessionDAO){
       DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
       sessionManager.setSessionIdCookie(simpleCookie);
       sessionManager.setSessionIdCookieEnabled(true);
       sessionManager.setSessionDAO(sessionDAO);
       sessionManager.setGlobalSessionTimeout(1800000L);
       return sessionManager;
   }
   @Bean
   public SessionDAO sessionDAO(StringRedisTemplate stringRedisTemplate){
       RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
       redisSessionDAO.setStringRedisTemplate(stringRedisTemplate);
       redisSessionDAO.setSerialization(new JDKSerialization());
       return redisSessionDAO;
   }
   @Bean
   public SimpleCookie simpleCookie(){
       SimpleCookie simpleCookie = new SimpleCookie();
       simpleCookie.setPath("/");
       simpleCookie.setDomain(cookieDomain);
       simpleCookie.setName("SCJSESSIONID");
       simpleCookie.setMaxAge(SimpleCookie.ONE_YEAR);
       return simpleCookie;
   }
}

留了擴展方法buildFilterChainDefinitionMap給子類用于實現自定義的攔截,例如

@Configuration
public class ShiroConfig extends AbstractShiroConfig{
    @Override
    public Map<String, String> buildFilterChainDefinitionMap() {
        Map<String, String> config = new HashMap<>();
        config.put("/**","authc");
        return config;
    }
}

這就是對該系統所有請求都需要進行登陸驗證

這個Filter如何整合到Servlet容器里面去,看上面代碼的第一個bean

@Bean
    public FilterRegistrationBean filterRegistrationBean(){
        FilterRegistrationBean filterRegistrationBean =new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new DelegatingFilterProxy());
        filterRegistrationBean.setName("shiroFilter");
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.addInitParameter("targetFilterLifecycle","true");
        return filterRegistrationBean;
    }

這是Spring提供的免配置化的注冊方式

在配置了ShiroFilter之后,對于需要驗證的請求,都會通過sessionid去取Session,判斷Session是否有效,如果無效,跳轉到單點登陸頁面進行登陸以及信息綁定,如果有效,進行正常操作。如果大家想了解更多相關知識,可以關注一下動力節點的Shiro視頻教程,里面有更豐富的知識等著大家去學習,希望對大家能夠有所幫助。

提交申請后,顧問老師會電話與您溝通安排學習

免費課程推薦 >>
技術文檔推薦 >>
主站蜘蛛池模板: 瑟瑟网站免费网站入口 | 丁香五香天堂网 | 天天尻| 三级伦理剧 | 性性影院在线观看 | 一级免费黄色 | 日韩在线观看高清 | 欧美日本视频一区 | 国产精品一区牛牛影视 | 天天拍夜夜添久久精品中文 | 黄色毛片国产 | 性夜黄 a 爽免费看 性亚洲 | 1769国产精品一区2区 | v视界影院视频一区二区三区 | 天堂在线视频精品 | 国产男女视频 | 五月天婷婷精品免费视频 | 涩涩网址 | 亚洲精品第一国产综合野 | 在线视频不卡国产在线视频不卡 | 国产手机视频 | h片在线 | 国产亚洲一区二区三区在线 | 55夜色66夜色欧洲精品 | 亚洲欧美视频一区二区三区 | 欧洲妇女成人淫片aaa视频 | 国产卡1卡2卡三卡在线 | 天天综合在线视频 | h成年动漫同人网站免费 | 麻豆久久婷婷国产综合五月 | 久久久网站亚洲第一 | 天天做天天爱夜夜爽女人爽宅 | 欧美亚洲高清日韩成人 | 在线观看国产一区二区三区99 | 亚洲视频一区 | 99精品欧美一区二区三区美图 | 天天综合天天做天天综合 | 在线观看成人影院 | 亚洲国产欧美在线 | 一级黄色片片 | 亚洲欧美日韩综合一区久久 |