大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

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

Shiro實現單點登錄的代碼

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

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在线 | 色婷婷色99国产综合精品 | 国产精品欧美亚洲韩国日本不卡 | 五月婷综合 | 亚洲香蕉国产高清在线播放 | 天天综合网久久 | 亚洲系列中文字幕一区二区 | 成人网视频 | 国产一级做a爱免费视频 | 日韩国产成人 | 午夜日韩| 国产在线观看中文字幕 | 亚洲精品国产一区二区在线 | 日日夜夜天天操 | 成人免费小视频 | 国产伦精品一区二区三区无广告 | 亚欧在线精品免费观看一区 | 四虎永久成人免费 | 国产精品福利视频免费观看 | 亚洲精品久久久久综合网 | 美女精品 | 亚欧精品在线观看 | 久久天天躁狠狠躁夜夜 | 免费特黄一级欧美大片在线看 | 91在线精品 | 欧美色亚洲 | 天天搞夜夜 | 亚洲精品久久久久午夜福 | 女人18毛片黄 | 国产日韩欧美二区 | 欧美成人天天综合天天在线 | 在线免费观看一级毛片 |