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

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

Shiro實現單點登錄的代碼

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

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視頻教程,里面有更豐富的知識等著大家去學習,希望對大家能夠有所幫助。

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

免費課程推薦 >>
技術文檔推薦 >>
主站蜘蛛池模板: 国产欧美日韩在线不卡第一页 | 日日夜夜伊人 | 2018天天干天天操 | 黄色免费大片 | 国产成人精品高清在线 | 亚洲一级特黄特黄的大片 | 国产精视频 | 国内自拍视频一区二区三区 | 一级做a爰片久久毛片武则天 | 免费观看大片毛片 | 欧美zozozo人禽交免费大片 | 小处雏一区二区三区精 | 韩国成人影院 | 国产精品人伦久久 | 日韩操操 | 欧美性群另类大交人妖 | 欧美成人精品手机在线观看 | 黄色按摩视频 | 日日摸日日碰夜夜爽视频网站 | 欧美成 人激情视频 | 黄色影视 | 欧美视频免费播放 | 国产成年网站 | 国产成人激情视频 | www午夜免费漫画 | 中文字幕在线观看一区二区 | 国产一区二区三区在线影院 | 午夜影院啊啊啊 | 国产精品秒播无毒不卡 | 91视频导航 | 曰批女人视频在线观看 | 欧美日韩在线精品成人综合网 | 99超级碰碰成人香蕉网 | 午夜影院91 | 97久久人人做人人爽人人澡 | 亚洲精品亚洲人成在线播放 | 波多野结衣一二区 | 老色批在线播放视频网站免费 | 国产精品成人一区二区三区 | 成人午夜爽爽爽免费视频 | 亚洲高清成人欧美动作片 |